diff --git a/bsp/rp2040/m_default/i2c_tinyusb.c b/bsp/rp2040/m_default/i2c_tinyusb.c index d7f207b..eb7844b 100644 --- a/bsp/rp2040/m_default/i2c_tinyusb.c +++ b/bsp/rp2040/m_default/i2c_tinyusb.c @@ -434,7 +434,7 @@ enum itu_status i2ctu_dev_write(enum ki2c_flags flags, enum itu_command startsto return ITU_STATUS_ADDR_ACK; } else*/ { - int rv = i2cex_write_timeout_us(PINOUT_I2C_DEV, addr, bit10, buf, len, nostop, 1000 * 1000); + int rv = i2cex_write_timeout_us(PINOUT_I2C_DEV, addr, bit10, buf, len, nostop, 400 * 1000); if (rv < 0 || (size_t)rv < len) return ITU_STATUS_ADDR_NAK; return ITU_STATUS_ADDR_ACK; } @@ -452,7 +452,7 @@ enum itu_status i2ctu_dev_read(enum ki2c_flags flags, enum itu_command startstop return ITU_STATUS_ADDR_ACK; } else*/ { - int rv = i2cex_read_timeout_us(PINOUT_I2C_DEV, addr, bit10, buf, len, nostop, 1000 * 1000); + int rv = i2cex_read_timeout_us(PINOUT_I2C_DEV, addr, bit10, buf, len, nostop, 400 * 1000); // printf("p le rv=%d buf=%02x ", rv, buf[0]); if (rv < 0 || (size_t)rv < len) return ITU_STATUS_ADDR_NAK; return ITU_STATUS_ADDR_ACK; diff --git a/dmctl3.py b/dmctl3.py new file mode 100755 index 0000000..2bc5233 --- /dev/null +++ b/dmctl3.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 + +import os, struct, sys + +f = os.open(sys.argv[1], os.O_RDWR | os.O_CLOEXEC) # TODO: windows: os.O_BINARY | +try: + os.write(f, b'\x00') # get version + resp = os.read(f, 4) # response: status, paylaod len (should be 2), payload + print("stat=%d plen=%d ver=%04x" % (resp[0], resp[1], struct.unpack(' +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if 0 +#include +#else +#include "dmj.h" +#endif + +#define HARDWARE_NAME "DapperMime-JTAG" +#define DEVICE_NAME "dmj-char" +#define CLASS_NAME "dmj" + +#define DMJ_READ_BUFSIZE 64 + +struct dmj_char_dev { + struct cdev cdev; + struct device *dev; + struct platform_device *pdev; + int minor; + spinlock_t devopen_lock; + + size_t bufpos; + uint8_t rdbuf[DMJ_READ_BUFSIZE]; +}; + +static int n_cdevs = 0; +static spinlock_t ndevs_lock; + +static int dmj_char_major; +static struct class *dmj_char_class; + +static ssize_t dmj_char_read(struct file *file, char *buf, size_t len, loff_t *loff) +{ + int res, bsize; + struct dmj_char_dev *dmjch; + size_t bpos, todo, done = 0; + uint8_t *kbuf; + struct device *dev; + + kbuf = kmalloc(len + DMJ_READ_BUFSIZE, GFP_KERNEL); + if (!kbuf) return -ENOMEM; + + dmjch = file->private_data; + dev = dmjch->dev; + todo = len; + + spin_lock(&dmjch->devopen_lock); + bpos = dmjch->bufpos; + + /* TODO: ioctl to control this buffering? */ + if (bpos) { /* data in buffer */ + if (len <= DMJ_READ_BUFSIZE - bpos) { + /* doable in a single copy, no USB xfers needed */ + memcpy(kbuf, &dmjch->rdbuf[bpos], len); + bpos += len; + if (bpos == DMJ_READ_BUFSIZE) bpos = 0; + + done += len; + todo -= len; + } else { + /* initial copy stuff */ + memcpy(kbuf, &dmjch->rdbuf[bpos], DMJ_READ_BUFSIZE - bpos); + todo -= DMJ_READ_BUFSIZE - bpos; + done += DMJ_READ_BUFSIZE - bpos; + bpos = 0; + } + } + + if /*while*/ (todo) { /* TODO: do we want a while here? */ + bsize = DMJ_READ_BUFSIZE; + + res = dmj_read(dmjch->pdev, 0, &kbuf[done], &bsize); + if (res < 0 || bsize < 0) { + /* ah snap */ + spin_unlock(&dmjch->devopen_lock); + return res; + } + + if ((size_t)bsize > todo) { + if ((size_t)bsize > todo + DMJ_READ_BUFSIZE) { + /* can't hold all this data, time to bail out... */ + dev_err(dev, "too much data (%zu B excess), can't buffer, AAAAAA", + (size_t)bsize - (todo + DMJ_READ_BUFSIZE)); + spin_unlock(&dmjch->devopen_lock); + BUG(); /* some stuff somewhere will have been corrupted, so, get out while we can */ + } + + /* stuff for next call */ + done = todo; + bpos = DMJ_READ_BUFSIZE - ((size_t)bsize - todo); + memcpy(&dmjch->rdbuf[bpos], &kbuf[done], (size_t)bsize - todo); + todo = 0; + } else { + todo -= (size_t)bsize; + done += (size_t)bsize; + } + } + + dmjch->bufpos = bpos; + spin_unlock(&dmjch->devopen_lock); + + res = copy_to_user(buf, kbuf, len); + if (res) return (res < 0) ? res : -EFAULT; + + return done; +} +static ssize_t dmj_char_write(struct file *file, const char *buf, size_t len, loff_t *off) +{ + unsigned long ret; + struct dmj_char_dev *dmjch; + void *kbuf; + + dmjch = file->private_data; + + kbuf = kmalloc(len, GFP_KERNEL); + if (!kbuf) return -ENOMEM; + + ret = copy_from_user(kbuf, buf, len); + if (ret) { + kfree(kbuf); + return (ret < 0) ? ret : -EFAULT; + } + + ret = dmj_transfer(dmjch->pdev, -1, 0, kbuf, len, NULL, NULL); + if (ret < 0) { + kfree(kbuf); + return ret; + } + + kfree(kbuf); + return len; +} + +static int dmj_char_open(struct inode *inode, struct file *file) +{ + struct dmj_char_dev *dmjch; + int ret; + + dmjch = container_of(inode->i_cdev, struct dmj_char_dev, cdev); + + spin_lock(&dmjch->devopen_lock); + ret = dmjch->bufpos; + if (~ret == 0) dmjch->bufpos = 0; + spin_unlock(&dmjch->devopen_lock); + + if (~ret != 0) return -ETXTBSY; // already open + + file->private_data = dmjch; + + return 0; +} +static int dmj_char_release(struct inode *inode, struct file *file) +{ + struct dmj_char_dev *dmjch; + + dmjch = container_of(inode->i_cdev, struct dmj_char_dev, cdev); + + spin_lock(&dmjch->devopen_lock); + dmjch->bufpos = ~(size_t)0; + spin_unlock(&dmjch->devopen_lock); + + return 0; +} + +static const struct file_operations dmj_char_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = dmj_char_read, + .write = dmj_char_write, + .unlocked_ioctl = NULL, + .open = dmj_char_open, + .release = dmj_char_release +}; + +static int dmj_char_probe(struct platform_device *pdev) +{ + int ret, minor; + struct device *device; + struct device *pd = &pdev->dev; + struct dmj_char_dev *dmjch; + + spin_lock(&ndevs_lock); + minor = n_cdevs; + ++n_cdevs; + spin_unlock(&ndevs_lock); + + dev_info(pd, HARDWARE_NAME " /dev entries driver, major=%d, minor=%d\n", + dmj_char_major, minor); + + dmjch = kzalloc(sizeof(*dmjch), GFP_KERNEL); + if (!dmjch) return -ENOMEM; + + platform_set_drvdata(pdev, dmjch); + + cdev_init(&dmjch->cdev, &dmj_char_fops); + ret = cdev_add(&dmjch->cdev, MKDEV(dmj_char_major, minor), 1); + if (ret < 0) { + dev_err(pd, "failed to create cdev: %d\n", ret); + return ret; + } + + device = device_create(dmj_char_class, pd, MKDEV(dmj_char_major, minor), dmjch, "dmj-%d", minor); + if (IS_ERR(device)) { + ret = PTR_ERR(device); + dev_err(pd, "failed to create device: %d\n", ret); + cdev_del(&dmjch->cdev); + return ret; + } + + dev_notice(device, "created device /dev/dmj-%d\n", minor); + + dmjch->dev = device; + dmjch->minor = minor; + dmjch->pdev = pdev; + dmjch->bufpos = ~(size_t)0; + + spin_lock_init(&dmjch->devopen_lock); + + return 0; +} +static int dmj_char_remove(struct platform_device *pdev) +{ + struct dmj_char_dev *dmjch = platform_get_drvdata(pdev); + + device_destroy(dmj_char_class, MKDEV(dmj_char_major, dmjch->minor)); + cdev_del(&dmjch->cdev); + unregister_chrdev(MKDEV(dmj_char_major, dmjch->minor), CLASS_NAME); + + kfree(dmjch); + + return 0; +} + +static struct platform_driver dmj_char_driver = { + .driver = { + .name = "dmj-char" + }, + .probe = dmj_char_probe, + .remove = dmj_char_remove +}; +/*module_platform_driver(dmj_char_driver);*/ + +static int __init dmj_char_init(void) +{ + int ret, major; + struct class *class; + dev_t devid; + + spin_lock_init(&ndevs_lock); + + n_cdevs = 0; + + devid = MKDEV(0,0); + ret = alloc_chrdev_region(&devid, 0, 256, DEVICE_NAME); + if (ret < 0) { + printk(KERN_ERR " failed to register chrdev major number: %d\n", major); + return ret; + } + major = MAJOR(devid); + printk(KERN_NOTICE DEVICE_NAME " registered with major number %d\n", major); + + class = class_create(THIS_MODULE, CLASS_NAME); + if (IS_ERR(class)) { + ret = PTR_ERR(class); + printk(KERN_ERR " failed to create class: %d\n", ret); + unregister_chrdev(major, DEVICE_NAME); /* TODO: unregister_chrdev_rage */ + return ret; + } + printk(KERN_DEBUG DEVICE_NAME " created class\n"); + + dmj_char_major = major; + dmj_char_class = class; + + platform_driver_register(&dmj_char_driver); + + return 0; +} +static void __exit dmj_char_exit(void) +{ + platform_driver_unregister(&dmj_char_driver); + + spin_lock(&ndevs_lock); + n_cdevs = 0; + spin_unlock(&ndevs_lock); + + class_destroy(dmj_char_class); + unregister_chrdev(MKDEV(dmj_char_major, 0), CLASS_NAME); /* TODO: unregister_chrdev_rage */ + + dmj_char_major = -1; + dmj_char_class = NULL; +} + +module_init(dmj_char_init); +module_exit(dmj_char_exit); + +MODULE_AUTHOR("sys64738 "); +MODULE_AUTHOR("haskal "); +MODULE_DESCRIPTION("Character device for the " HARDWARE_NAME " USB multitool"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:dmj-char"); + +MODULE_DEPEND diff --git a/host/modules/dmj.c b/host/modules/dmj.c new file mode 100644 index 0000000..fd99e96 --- /dev/null +++ b/host/modules/dmj.c @@ -0,0 +1,538 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for the DapperMime-JTAG USB multitool: base MFD driver + * + * Copyright (c) sys64738 and haskal + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if 0 +#include +#else +#include "dmj.h" +#endif + +#define HARDWARE_NAME "DapperMime-JTAG" +#define HARDWARE_NAME_SYMBOLIC "dappermime-jtag" + +#define DMJ_USB_TIMEOUT 500 + +#define DMJ_RESP_HDR_SIZE 4 + +/* endpoint indices, not addresses */ +#define DMJ_VND_CFG_EP_OUT 0 +#define DMJ_VND_CFG_EP_IN 1 + +struct dmj_dev { + struct usb_device *usb_dev; + struct usb_interface *interface; + uint8_t ep_in; + uint8_t ep_out; + + spinlock_t disconnect_lock; + bool disconnect; + + uint8_t dmj_mode, dmj_m1feature; +}; + +/* USB transfers */ + +static void *dmj_prep_buf(int cmd, const void *wbuf, int *wbufsize, gfp_t gfp) +{ + int len; + uint8_t *buf; + + if (cmd >= 0 && cmd <= 0xff) { + /* send extra cmd byte, and optionally a payload */ + if (wbufsize && wbuf) len = *wbufsize + 1; + else len = 1; + + buf = kmalloc(len, gfp); + if (!buf) return NULL; + + buf[0] = (uint8_t)cmd; + if (wbuf && *wbufsize) + memcpy(&buf[1], wbuf, *wbufsize); + } else { + /* assuming we have a payload to send, but no (explicit) cmd */ + len = *wbufsize; + buf = kmalloc(len, gfp); + if (!buf) return NULL; + + memcpy(buf, wbuf, len); + } + + *wbufsize = len; + + return buf; +} +static int dmj_send_wait(struct dmj_dev *dmj, int cmd, const void *wbuf, int wbufsize) +{ + int ret = 0; + int len = wbufsize, actual; + void *buf; + + buf = dmj_prep_buf(cmd, wbuf, &len, GFP_KERNEL); + if (!buf) return -ENOMEM; + + ret = usb_bulk_msg(dmj->usb_dev, usb_sndbulkpipe(dmj->usb_dev, dmj->ep_out), + buf, len, &actual, DMJ_USB_TIMEOUT); + + kfree(buf); + + return ret; +} + +static int dmj_recv_wait(struct dmj_dev *dmj, void **kbuf, int rbufsize, bool parse_hdr) +{ + int len, actual; + int ret; + void *buf; + + *kbuf = NULL; + + if (rbufsize <= 0) len = 0; + else if (parse_hdr) len = rbufsize + DMJ_RESP_HDR_SIZE; + else len = rbufsize; + + buf = kmalloc(len, GFP_KERNEL); + if (!buf) return -ENOMEM; + + ret = usb_bulk_msg(dmj->usb_dev, usb_rcvbulkpipe(dmj->usb_dev, dmj->ep_in), + buf, len, &actual, DMJ_USB_TIMEOUT); + if (ret < 0) kfree(buf); + + *kbuf = buf; + return actual; +} + +int dmj_xfer_internal(struct dmj_dev *dmj, int cmd, int recvflags, + const void *wbuf, int wbufsize, void *rbuf, int *rbufsize) +{ + int ret = 0, actual; + struct device *dev = &dmj->interface->dev; + int respstat, total_len, bytes_read; + uint32_t pl_len; + void *buf; + uint8_t *dbuf; + ptrdiff_t pl_off; + + spin_lock(&dmj->disconnect_lock); + if (dmj->disconnect) ret = -ENODEV; + spin_unlock(&dmj->disconnect_lock); + + if (ret) return ret; + + if ((cmd >= 0 && cmd <= 0xff) || (wbufsize && wbuf)) { + ret = dmj_send_wait(dmj, cmd, wbuf, wbufsize); + if (ret < 0) { + dev_err(dev, "USB write failed: %d\n", ret); + return ret; + } + } + + if ((recvflags & DMJ_XFER_FLAGS_PARSE_RESP) == 0 + && !(rbufsize && *rbufsize > 0 && rbuf)) { + /* don't want any type of response? then dont do the urb stuff either */ + return 0; + } + + /* first recv buffer, with optional response header parsing */ + ret = dmj_recv_wait(dmj, &buf, (rbufsize && *rbufsize > 0) ? *rbufsize : 0, + (recvflags & DMJ_XFER_FLAGS_PARSE_RESP) != 0); + if (ret < 0 || !buf) { + dev_err(dev, "USB read failed: %d\n", ret); + return ret; + } + actual = ret; + + if (recvflags & DMJ_XFER_FLAGS_PARSE_RESP) { + dbuf = buf; + + /* decode payload length */ + if (dbuf[1] & 0x80) { + if (actual < 3) { + dev_err(dev, "short response (%d, expected at least 3)\n", actual); + kfree(buf); + return -EREMOTEIO; + } + + pl_len = (uint32_t)(dbuf[1] & 0x7f); + + if (dbuf[2] & 0x80) { + if (actual < 4) { + dev_err(dev, "short response (%d, expected at least 4)\n", actual); + kfree(buf); + return -EREMOTEIO; + } + + pl_len |= (uint32_t)(dbuf[2] & 0x7f) << 7; + pl_len |= (uint32_t)dbuf[3] << 14; + pl_off = 4; + } else { + pl_len |= (uint32_t)dbuf[2] << 7; + pl_off = 3; + } + } else { + if (actual < 2) { + dev_err(dev, "short response (%d, expected at least 2)\n", actual); + kfree(buf); + return -EREMOTEIO; + } + + pl_len = (uint32_t)dbuf[1]; + pl_off = 2; + } + + respstat = dbuf[0]; + total_len = pl_len; + actual -= pl_off; + + /*dev_dbg(dev, "pl_len=%d,off=%ld,resp=%d\n", pl_len, pl_off, respstat);*/ + } else { + pl_off = 0; + if (rbufsize && *rbufsize > 0) total_len = *rbufsize; + else total_len = actual; + respstat = 0; + } + + if (rbuf && rbufsize && *rbufsize > 0) { + if (*rbufsize < total_len) total_len = *rbufsize; + + if (actual > total_len) { + /*if (recvflags & DMJ_XFER_FLAGS_FILL_RECVBUF)*/ + { + dev_err(dev, "aaa msgsize %d > %d\n", actual, total_len); + kfree(buf); + return -EMSGSIZE; + } /*else { + actual = total_len; + }*/ + } + + memcpy(rbuf + bytes_read, buf + pl_off, actual); + kfree(buf); + pl_off = -1; + buf = NULL; + + bytes_read = actual; + + while (bytes_read < total_len && (recvflags & DMJ_XFER_FLAGS_FILL_RECVBUF) != 0) { + ret = dmj_recv_wait(dmj, &buf, total_len - bytes_read, false); + if (ret < 0 || !buf) { + dev_err(dev, "USB read failed: %d\n", ret); + return ret; + } + actual = ret; + + if (bytes_read + actual > total_len) { + /*actual = total_len - bytes_read;*/ + dev_err(dev, "aaa2 msgsize %d > %d\n", actual+bytes_read, total_len); + kfree(buf); + return -EMSGSIZE; + } + memcpy(rbuf + bytes_read, buf, actual); + kfree(buf); + buf = NULL; + + bytes_read += actual; + } + } else { + bytes_read = 0; + kfree(buf); + } + + *rbufsize = bytes_read; + + /*dev_dbg(dev, "all good! resp=%02x\n", respstat);*/ + return respstat; +} + +int dmj_transfer(struct platform_device *pdev, int cmd, int recvflags, + const void *wbuf, int wbufsize, void *rbuf, int *rbufsize) +{ + struct dmj_platform_data *dmj_pdata; + struct dmj_dev *dmj; + + dmj = dev_get_drvdata(pdev->dev.parent); + dmj_pdata = dev_get_platdata(&pdev->dev); /* TODO: ??? */ + + return dmj_xfer_internal(dmj, cmd, recvflags, wbuf, wbufsize, rbuf, rbufsize); +} +EXPORT_SYMBOL(dmj_transfer); + +/* stuff on init */ + +static int dmj_check_hw(struct dmj_dev *dmj) +{ + struct device *dev = &dmj->interface->dev; + + int ret; + __le16 protover; + int len = sizeof(protover); + + ret = dmj_xfer_internal(dmj, DMJ_CMD_CFG_GET_VERSION, + DMJ_XFER_FLAGS_PARSE_RESP, NULL, 0, &protover, &len); + + if (ret < 0) { /* TODO: positive error codes from respstat */ + dev_err(dev, "USB fail: %d\n", ret); + return ret; + } + if (len < sizeof(protover)) { + dev_err(dev, "USB fail remoteio: %d\n", len); + return -EREMOTEIO; + } + if (len > sizeof(protover)) { + dev_err(dev, "USB fail msgsize: %d\n", len); + return -EMSGSIZE; + } + + if (le16_to_cpu(protover) != DMJ_USB_CFG_PROTO_VER) { + dev_err(&dmj->interface->dev, HARDWARE_NAME " config protocol version 0x%04x too %s\n", + le16_to_cpu(protover), (le16_to_cpu(protover) > DMJ_USB_CFG_PROTO_VER) ? "new" : "old"); + return -ENODEV; + } + + return 0; +} +static int dmj_print_info(struct dmj_dev *dmj) +{ + int ret, i, j, len; + __le16 modes, mversion; + uint8_t curmode, features; + struct device *dev = &dmj->interface->dev; + char strinfo[65]; + char modeinfo[16]; + + /* info string */ + len = sizeof(strinfo)-1; + ret = dmj_xfer_internal(dmj, DMJ_CMD_CFG_GET_INFOSTR, + DMJ_XFER_FLAGS_PARSE_RESP, NULL, 0, strinfo, &len); + if (ret < 0) return ret; /* TODO: positive error codes from respstat */ + if (len >= sizeof(strinfo)) return -EMSGSIZE; + strinfo[len] = 0; /*strinfo[64] = 0;*/ + dev_info(dev, HARDWARE_NAME " '%s'\n", strinfo); + + /* cur mode */ + len = sizeof(curmode); + ret = dmj_xfer_internal(dmj, DMJ_CMD_CFG_GET_CUR_MODE, + DMJ_XFER_FLAGS_PARSE_RESP, NULL, 0, &curmode, &len); + if (ret < 0) return ret; + if (len < sizeof(curmode)) return -EREMOTEIO; + if (len > sizeof(curmode)) return -EMSGSIZE; + dmj->dmj_mode = curmode; + + /* map of available modes */ + len = sizeof(modes); + ret = dmj_xfer_internal(dmj, DMJ_CMD_CFG_GET_MODES, + DMJ_XFER_FLAGS_PARSE_RESP, NULL, 0, &modes, &len); + if (ret < 0) return ret; + if (len < sizeof(modes)) return -EREMOTEIO; + if (len > sizeof(modes)) return -EMSGSIZE; + + for (i = 1; i < 16; ++i) { /* build the string, uglily */ + if (le16_to_cpu(modes) & (1<= sizeof(strinfo)) return -EMSGSIZE; + strinfo[len] = 0; /*strinfo[64] = 0;*/ + + /* version */ + len = sizeof(mversion); + ret = dmj_xfer_internal(dmj, (i<<4) | DMJ_CMD_MODE_GET_VERSION, + DMJ_XFER_FLAGS_PARSE_RESP, NULL, 0, &mversion, &len); + if (ret < 0) return ret; + if (len < sizeof(mversion)) return -EREMOTEIO; + if (len > sizeof(mversion)) return -EMSGSIZE; + + /* features */ + len = sizeof(features); + ret = dmj_xfer_internal(dmj, (i<<4) | DMJ_CMD_MODE_GET_FEATURES, + DMJ_XFER_FLAGS_PARSE_RESP, NULL, 0, &features, &len); + if (ret < 0) return ret; + if (len < sizeof(features)) return -EREMOTEIO; + if (len > sizeof(features)) return -EMSGSIZE; + + if (i == 1) dmj->dmj_m1feature = features; + + for (j = 0; j < 8; ++j) { + if (features & (1<cur_altsetting; + struct usb_endpoint_descriptor *epin, *epout; + struct device *dev = &itf->dev; + struct dmj_dev *dmj; + int ret; + + /*dev_dbg(dev, "dmj probe itfn=%d nendp=%d\n", + hostitf->desc.bInterfaceNumber, hostitf->desc.bNumEndpoints);*/ + + if (hostitf->desc.bInterfaceNumber != 0 || hostitf->desc.bNumEndpoints < 2) + return -ENODEV; + + /* TODO: query endpoints in a better way */ + epout = &hostitf->endpoint[DMJ_VND_CFG_EP_OUT].desc; + epin = &hostitf->endpoint[DMJ_VND_CFG_EP_IN].desc; + + /*dev_dbg(dev, "epout addr=0x%02x, epin addr=0x%02x\n", + epout->bEndpointAddress, epin->bEndpointAddress);*/ + + if (!usb_endpoint_is_bulk_out(epout)) { dev_warn(dev, "aaa1\n"); return -ENODEV; } + if (!usb_endpoint_is_bulk_in(epin)) { dev_warn(dev, "aaa1\n"); return -ENODEV; } + + dmj = kzalloc(sizeof(*dmj), GFP_KERNEL); + if (!dmj) return -ENOMEM; + + /*dev_dbg(dev, "hi there!\n");*/ + + dmj->ep_out = epout->bEndpointAddress; + dmj->ep_in = epin->bEndpointAddress; + dmj->usb_dev = usb_get_dev(interface_to_usbdev(itf)); + dmj->interface = itf; + usb_set_intfdata(itf, dmj); + + spin_lock_init(&dmj->disconnect_lock); + + ret = dmj_hw_init(dmj); + if (ret < 0) { + dev_err(dev, "failed to initialize hardware\n"); + goto out_free; + } + + if (dmj->dmj_mode == 1) { + ret = mfd_add_hotplug_devices(dev, dmj_mfd_devs, ARRAY_SIZE(dmj_mfd_devs)); + if (ret) { + dev_err(dev, "failed to add MFD devices\n"); + goto out_free; + } + + if (dmj->dmj_mode & DMJ_FEATURE_MODE1_SPI) { + // TODO: add SPI MFD + } + if (dmj->dmj_mode & DMJ_FEATURE_MODE1_I2C) { + // TODO: add I2C MFD + } + if (dmj->dmj_mode & DMJ_FEATURE_MODE1_TEMPSENSOR) { + // TODO: add tempsensor MFD + } + } + + return 0; + +out_free: + usb_put_dev(dmj->usb_dev); + kfree(dmj); + + return ret; +} +static void dmj_disconnect(struct usb_interface *itf) +{ + struct dmj_dev *dmj = usb_get_intfdata(itf); + + spin_lock(&dmj->disconnect_lock); + dmj->disconnect = true; + spin_unlock(&dmj->disconnect_lock); + + mfd_remove_devices(&itf->dev); + + usb_put_dev(dmj->usb_dev); + + kfree(dmj); +} + +static int dmj_suspend(struct usb_interface *itf, pm_message_t message) +{ + struct dmj_dev *dmj = usb_get_intfdata(itf); + + (void)message; + + spin_lock(&dmj->disconnect_lock); + dmj->disconnect = true; + spin_unlock(&dmj->disconnect_lock); + + return 0; +} +static int dmj_resume(struct usb_interface *itf) +{ + struct dmj_dev *dmj = usb_get_intfdata(itf); + + dmj->disconnect = false; + + return 0; +} + +static const struct usb_device_id dmj_table[] = { + { USB_DEVICE(0xcafe, 0x1312) }, + { } +}; +MODULE_DEVICE_TABLE(usb, dmj_table); + +static struct usb_driver dmj_driver = { + .name = HARDWARE_NAME_SYMBOLIC, + .probe = dmj_probe, + .disconnect = dmj_disconnect, + .id_table = dmj_table, + .suspend = dmj_suspend, + .resume = dmj_resume, +}; +module_usb_driver(dmj_driver); + +MODULE_AUTHOR("sys64738 "); +MODULE_AUTHOR("haskal "); +MODULE_DESCRIPTION("Core driver for the " HARDWARE_NAME " USB multitool"); +MODULE_LICENSE("GPL v2"); + +MODULE_SOFTDEP("post: dmj-char"); diff --git a/host/modules/dmj.h b/host/modules/dmj.h new file mode 100644 index 0000000..10b2b1f --- /dev/null +++ b/host/modules/dmj.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LINUX_USB_DAPPERMIMEJTAG_H +#define __LINUX_USB_DAPPERMIMEJTAG_H + +#define DMJ_USB_CFG_PROTO_VER 0x0010 + +#define DMJ_RESP_STAT_OK 0x00 +#define DMJ_RESP_STAT_ILLCMD 0x01 +#define DMJ_RESP_STAT_BADMODE 0x02 +#define DMJ_RESP_STAT_NOSUCHMODE 0x03 +#define DMJ_RESP_STAT_BADARG 0x04 + +#define DMJ_CMD_CFG_GET_VERSION 0x00 +#define DMJ_CMD_CFG_GET_MODES 0x01 +#define DMJ_CMD_CFG_GET_CUR_MODE 0x02 +#define DMJ_CMD_CFG_SET_CUR_MODE 0x03 +#define DMJ_CMD_CFG_GET_INFOSTR 0x04 + +#define DMJ_CMD_MODE_GET_NAME 0x00 +#define DMJ_CMD_MODE_GET_VERSION 0x01 +#define DMJ_CMD_MODE_GET_FEATURES 0x02 + +#define DMJ_CMD_MODE1_SPI 0x13 +#define DMJ_CMD_MODE1_I2C 0x14 +#define DMJ_CMD_MODE1_TEMPSENSOR 0x15 + +#define DMJ_FEATURE_MODE1_UART (1<<0) +#define DMJ_FEATURE_MODE1_CMSISDAP (1<<1) +#define DMJ_FEATURE_MODE1_SPI (1<<2) +#define DMJ_FEATURE_MODE1_I2C (1<<3) +#define DMJ_FEATURE_MODE1_TEMPSENSOR (1<<4) + +struct dmj_platform_data { + uint8_t port; +}; + +#define DMJ_XFER_FLAGS_PARSE_RESP (1<<0) +#define DMJ_XFER_FLAGS_FILL_RECVBUF (1<<1) + +int dmj_transfer(struct platform_device *pdev, int cmd, int recvflags, + const void *wbuf, int wbufsize, void *rbuf, int *rbufsize); + +inline static int dmj_read(struct platform_device *pdev, int recvflags, + void *rbuf, int *rbufsize) +{ + return dmj_transfer(pdev, -1, recvflags, NULL, 0, rbuf, rbufsize); +} + +inline static int dmj_write(struct platform_device *pdev, int cmd, + const void *wbuf, int wbufsize) +{ + return dmj_transfer(pdev, cmd, DMJ_XFER_FLAGS_PARSE_RESP, wbuf, wbufsize, NULL, NULL); +} + +#endif diff --git a/src/m_default/_default.c b/src/m_default/_default.c index aad22b9..f29ea2d 100644 --- a/src/m_default/_default.c +++ b/src/m_default/_default.c @@ -29,7 +29,7 @@ /*#define MODE_ENABLE_I2CTINYUSB*/ enum m_default_cmds { - mdef_cmd_spi = mode_cmd__specific, + mdef_cmd_spi = mode_cmd__specific | 0x10, mdef_cmd_i2c, mdef_cmd_tempsense }; diff --git a/src/m_default/cdc_serprog.c b/src/m_default/cdc_serprog.c index 5850dcf..490abb8 100644 --- a/src/m_default/cdc_serprog.c +++ b/src/m_default/cdc_serprog.c @@ -189,15 +189,19 @@ static void handle_cmd(uint8_t cmd, int ud, uint8_t (*read_byte)(void), break; case S_CMD_SPIOP: case S_CMD_SPI_READ: case S_CMD_SPI_WRITE: { - uint32_t slen, rlen; + uint32_t slen = 0, rlen = 0; // clang-format off - slen = (uint32_t)read_byte(); - slen |= (uint32_t)read_byte() << 8; - slen |= (uint32_t)read_byte() << 16; - rlen = (uint32_t)read_byte(); - rlen |= (uint32_t)read_byte() << 8; - rlen |= (uint32_t)read_byte() << 16; + if (cmd == S_CMD_SPIOP || cmd == S_CMD_SPI_WRITE) { + slen = (uint32_t)read_byte(); + slen |= (uint32_t)read_byte() << 8; + slen |= (uint32_t)read_byte() << 16; + } + if (cmd == S_CMD_SPIOP || cmd == S_CMD_SPI_READ) { + rlen = (uint32_t)read_byte(); + rlen |= (uint32_t)read_byte() << 8; + rlen |= (uint32_t)read_byte() << 16; + } // clang-format on if (writehdr) diff --git a/src/m_default/vnd_i2ctinyusb.c b/src/m_default/vnd_i2ctinyusb.c index 533022d..5ab7477 100644 --- a/src/m_default/vnd_i2ctinyusb.c +++ b/src/m_default/vnd_i2ctinyusb.c @@ -212,7 +212,8 @@ void i2ctu_bulk_cmd(void) { uint16_t us; uint32_t func, freq; - switch (vnd_cfg_read_byte()) { + uint8_t cmdb = vnd_cfg_read_byte(); + switch (cmdb) { case ITU_CMD_ECHO: txbuf[0] = vnd_cfg_read_byte(); vnd_cfg_write_resp(cfg_resp_ok, 1, txbuf); @@ -247,7 +248,7 @@ void i2ctu_bulk_cmd(void) { case ITU_CMD_I2C_IO_END: case ITU_CMD_I2C_IO_BEGINEND: { struct itu_cmd cmd; - cmd.cmd = vnd_cfg_read_byte(); + cmd.cmd = cmdb; cmd.flags = (uint16_t)vnd_cfg_read_byte(); cmd.flags |= (uint16_t)vnd_cfg_read_byte() << 8; cmd.addr = (uint16_t)vnd_cfg_read_byte(); diff --git a/src/vnd_cfg.h b/src/vnd_cfg.h index c6c5346..f139ab9 100644 --- a/src/vnd_cfg.h +++ b/src/vnd_cfg.h @@ -5,7 +5,8 @@ #include -/* the configuration vendor interface must always be the first vendor itf. */ +/* the configuration vendor interface must always be the first vendor itf, and + * have endpoint number 1. */ #define VND_CFG_PROTO_VER 0x0010 @@ -45,38 +46,5 @@ void vnd_cfg_write_flush(void); void vnd_cfg_write_byte(uint8_t v); void vnd_cfg_write_resp(enum cfg_resp stat, uint32_t len, const void* data); -/* - * wire protocol: - * host sends messages, device sends replies. the device never initiates a xfer - * - * the first byte of a command is the combination of the mode it is meant for - * in the high nybble, and the command number itself in the low nybble. optional - * extra command bytes may follow, depending on the command itself. - * a high nybble is 0 signifies a general configuration command, not meant for - * a particular mode - * - * a response consists of a response status byte (enum cfg_resp), followed by - * a 7- or 15-bit VLQ int (little-endian) for the payload length, followed by - * the payload itself. - * - * general commands: - * * get vesion (0x00): returns a payload of 2 bytes with version data. should - * currently be 0x10 0x00 (00.10h) - * * get modes (0x01): returns 2 bytes with a bitmap containing all supported - * modes (bit 0 is for general cfg and must always be 1) - * * get cur mode (0x02): returns a single byte containing the current mode number - * * set cur mode (0x03): sets the current mode. one extra request byte, no - * response payload - * * get info string (0x04): get a string containing human-readable info about - * the device. for display only - * - * common mode commands: - * * get name (0x00): returns a name or other descriptive string in the payload. - * for display only - * * get version (0x01): returns a 2-byte version number in the payload - * * get_features (0x02): gets a bitmap of supported features. length and meaning - * of the bits depends on the mode - */ - #endif