rebrand (we have finally found a non-temporary name)

This commit is contained in:
Triss 2021-08-17 23:42:57 +02:00
parent c21a9ec431
commit 6c970432fe
27 changed files with 1512 additions and 1480 deletions

View File

@ -3,7 +3,7 @@ set(FAMILY "rp2040" CACHE STRING "Board/MCU family, decides which drivers to use
set(BOARD "raspberry_pi_pico" CACHE STRING "Board used, determines the pinout. Defaults to the Raspberry Pi Pico.")
# use directory name for project id # FIXME: use fixed project name
get_filename_component(PROJECT ${CMAKE_CURRENT_SOURCE_DIR} NAME)
get_filename_component(PROJECT "Dragon Probe" NAME)
set(PROJECT ${BOARD}-${PROJECT})
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

View File

@ -1,6 +1,4 @@
# DapperMime-JTAG
(name is still WIP)
# Dragon Probe
This project attempts to add Bus Pirate/...-like functionality to a number of
MCUs, mainly the Raspberry Pi Pico. It was originally based on [Dapper
@ -81,7 +79,6 @@ projects. These respective licenses can be found in
## TODO
- [ ] **A name**
- [ ] **A (VID and) PID, and maybe better subclass & protocol IDs for the vnd cfg itf**
- [ ] GPL license headers on every file
- [x] Debug interface to send printf stuff directly to USB, instead of having
@ -102,6 +99,8 @@ projects. These respective licenses can be found in
- interface 2 ("C"): index 3, epin 0x06, epout 0x85
- interface 3 ("D"): index 4, epin 0x08, epout 0x87
- ~~or, FX2 emulation mode??? (useful links: https://sigrok.org/wiki/Fx2lafw ; https://sigrok.org/wiki/CWAV_USBee_SX/Info )~~ has a ROM/fw and everything, so, maybe not
- [ ] "Complex Trigger" mode for aiding with glitching triggers, by turning
UART/SPI/I2C/eMMC/... sequences into a GPIO toggle
- [ ] Mode where you can define custom PIO stuff for custom pinouts/protocols??????
- Maybe also with code that auto-reacts to stuff from the environment?
- [ ] Facedancer implementation by connecting two picos via GPIO, one doing host

View File

@ -22,18 +22,24 @@ static bool mode_enabled = false;
// enable: enable flag
// return: 1 - Success, 0 - Error
uint32_t SWO_Mode_UART(uint32_t enable) {
//for(;;);//printf("SWO mode %lu\n", enable);
if (enable) {
swo_sm = pio_claim_unused_sm(SWO_PIO, false);
if (swo_sm == -1) return 0;
if (swo_sm == -1) {
//for(;;);//printf("E: no PIO\n");
return 0;
}
swo_dmach = dma_claim_unused_channel(false);
if (swo_dmach == -1) {
//for(;;);//printf("E: no DMA\n");
pio_sm_unclaim(SWO_PIO, swo_sm);
swo_sm = -1;
return 0;
}
if (!pio_can_add_program(SWO_PIO, &uart_rx_program)) {
//for(;;);//printf("E: no prg\n");
dma_channel_unclaim(swo_dmach);
swo_dmach = -1;
pio_sm_unclaim(SWO_PIO, swo_sm);
@ -75,6 +81,7 @@ uint32_t SWO_Mode_UART(uint32_t enable) {
// baudrate: requested baudrate
// return: actual baudrate or 0 when not configured
uint32_t SWO_Baudrate_UART(uint32_t baudrate) {
//for(;;);//printf("SWO baudrate %lu\n", baudrate);
if (!mode_enabled) return 0;
uart_rx_program_init(SWO_PIO, swo_sm, swo_pio_off, PINOUT_SWO, baudrate);
@ -86,6 +93,7 @@ uint32_t SWO_Baudrate_UART(uint32_t baudrate) {
// active: active flag
// return: 1 - Success, 0 - Error
uint32_t SWO_Control_UART(uint32_t active) {
//for(;;);//printf("SWO control %lu\n", active);
if (!mode_enabled) return 0;
if (active) {
@ -105,6 +113,7 @@ uint32_t SWO_Control_UART(uint32_t active) {
// buf: pointer to buffer for capturing
// num: number of bytes to capture
void SWO_Capture_UART(uint8_t* buf, uint32_t num) {
//for(;;);//printf("SWO capture %p 0x%lx\n", buf, num);
if (!mode_enabled) return;
swo_num = num;
@ -128,6 +137,7 @@ uint32_t SWO_GetCount_UART(void) {
if (!mode_enabled || swo_num == 0) return 0; // not initialized
uint32_t remaining = dma_hw->ch[swo_dmach].transfer_count;
//for(;;);//printf("SWO getcount -> 0x%lx\n", swo_num - remaining);
return swo_num - remaining;
}

10
host/42-dragonprobe.rules Normal file
View File

@ -0,0 +1,10 @@
SUBSYSTEMS=="usb", ATTRS{idVendor}=="cafe", ATTRS{idProduct}=="1312", MODE:="0666", ENV{IS_DRAGONPROBE}:="yes"
# TODO: redo all this here once the interface has settled a bit more
# (it needs the regular UART, SUMP CDC, SPI2 CDC (to be implemented), ...)
#ENV{IS_DRAGONPROBE}=="yes", ATTRS{interface}=="stdio CDC interface (debug)", ENV{DRAGONPROBE_INTERFACE_TYPE}:="debug"
#ENV{IS_DRAGONPROBE}=="yes", ATTRS{interface}=="Serprog CDC interface", ENV{DRAGONPROBE_INTERFACE_TYPE}:="serprog"
#ENV{IS_DRAGONPROBE}=="yes", ATTRS{interface}=="CMSIS-DAP HID interface", ENV{DRAGONPROBE_INTERFACE_TYPE}:="dap"
#ENV{DRAGONPROBE_INTERFACE_TYPE}=="debug", SUBSYSTEM=="tty", SYMLINK+="dragnbus-dbg"
#ENV{DRAGONPROBE_INTERFACE_TYPE}=="serprog", SUBSYSTEM=="tty", SYMLINK+="dragnbus-serprog"
#ENV{DRAGONPROBE_INTERFACE_TYPE}=="dap", SUBSYSTEM=="usbmisc", SYMLINK+="dragnbus-dap"

View File

@ -1,3 +0,0 @@
#!/bin/sh
cd "$(dirname "$(realpath "$0")")"
exec python3 -m dmctl "$@"

3
host/dpctl.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
cd "$(dirname "$(realpath "$0")")"
exec python3 -m "$(basename -s .sh "$0")" "$@"

View File

@ -6,12 +6,12 @@ import traceback
from typing import *
import dmctl.connection as devconn
import dmctl.protocol as devproto
import dmctl.commands as devcmds
import dpctl.connection as devconn
import dpctl.protocol as devproto
import dpctl.commands as devcmds
def dmctl_do(args: Any) -> int:
def dpctl_do(args: Any) -> int:
def get_device_info(conn, args): return devcmds.get_device_info(conn)
def get_mode_info(conn, args): return devcmds.get_mode_info(conn, args.mode)
def set_mode(conn, args): return devcmds.set_mode(conn, args.mode)
@ -80,12 +80,12 @@ def dmctl_do(args: Any) -> int:
print("Could not connect to a device: %s." % conn)
return 1
with devproto.DmjDevice(conn) as dev:
with devproto.DPDevice(conn) as dev:
return subfn(dev, args)
def main() -> int:
parser = argparse.ArgumentParser(prog="dmctl")
parser = argparse.ArgumentParser(prog="dpctl")
def auto_int(x):
return int(x, 0)
@ -114,10 +114,10 @@ def main() -> int:
# * mode 5 (ftdi/fx2 emul): probably nothing
parser.add_argument('--conn', type=str, default=None,
help="Connection string. Either a dmj-char device in"+\
" /dev, a USB bus.device number, or a USB VID:PID " + \
"pair. Defaults to trying /dev/dmj-* (if there is " + \
"only one), and cafe:1312 otherwise.")
help="Connection string. Either a dragonprobe-char "+\
"device in /dev, a USB bus.device number, or a USB " + \
"VID:PID pair. Defaults to trying /dev/dragonprobe-* " + \
"(if there is only one), and cafe:1312 otherwise.")
#parser.descripiton = ...
subcmds = parser.add_subparsers(required=True, metavar="subcommand",
@ -192,5 +192,5 @@ def main() -> int:
help="Disable overclocking, short for --set 0")
args = parser.parse_args()
return dmctl_do(args)
return dpctl_do(args)

View File

@ -1,9 +1,9 @@
import dmctl
import dpctl
try:
exit(dmctl.main() or 0)
exit(dpctl.main() or 0)
except Exception:# as e
import traceback
traceback.print_exc()

View File

@ -15,7 +15,7 @@ FEATURES_OF_MODE = {
}
def get_device_info(dev: DmjDevice) -> int:
def get_device_info(dev: DPDevice) -> int:
print("%s: protocol version: %02x.%02x, currently in mode %d (%s)" % \
(dev.infotext, dev.protocol_version >> 8, dev.protocol_version & 0xff,
dev.current_mode, dev.mode_info[dev.current_mode].infotext)
@ -31,7 +31,7 @@ def get_device_info(dev: DmjDevice) -> int:
return 0
def get_mode_info(dev: DmjDevice, mode: Optional[str]) -> int:
def get_mode_info(dev: DPDevice, mode: Optional[str]) -> int:
def try_parse(s: str):
try: return int(s)
except ValueError: return None
@ -73,7 +73,7 @@ def get_mode_info(dev: DmjDevice, mode: Optional[str]) -> int:
return 1
def set_mode(dev: DmjDevice, mode: int) -> int:
def set_mode(dev: DPDevice, mode: int) -> int:
try:
dev.set_mode(mode)
return 0
@ -85,7 +85,7 @@ def set_mode(dev: DmjDevice, mode: int) -> int:
# ---
def uart_hw_flowctl_get(dev: DmjDevice) -> int:
def uart_hw_flowctl_get(dev: DPDevice) -> int:
try:
res = dev.m1_usb_hw_flowctl_get()
print("Flow control %sabled" % ("en" if res else "dis"))
@ -95,7 +95,7 @@ def uart_hw_flowctl_get(dev: DmjDevice) -> int:
return 1
def uart_hw_flowctl_set(dev: DmjDevice, v: bool) -> int:
def uart_hw_flowctl_set(dev: DPDevice, v: bool) -> int:
try:
dev.m1_usb_hw_flowctl_set(v)
return 0
@ -107,7 +107,7 @@ def uart_hw_flowctl_set(dev: DmjDevice, v: bool) -> int:
# ---
def tempsensor_get(dev: DmjDevice) -> int:
def tempsensor_get(dev: DPDevice) -> int:
try:
res = dev.m1_tempsensor_i2cemul_get()
if res is None:
@ -120,7 +120,7 @@ def tempsensor_get(dev: DmjDevice) -> int:
return 1
def tempsensor_set(dev: DmjDevice, v: int) -> int:
def tempsensor_set(dev: DPDevice, v: int) -> int:
try:
old, new = dev.m1_tempsensor_i2cemul_set(v)
olds = "disabled" if old is None else ("0x%02x" % old)
@ -135,7 +135,7 @@ def tempsensor_set(dev: DmjDevice, v: int) -> int:
# ---
def jtag_scan(dev: DmjDevice, typ: str, start_pin: int, end_pin: int) -> int:
def jtag_scan(dev: DPDevice, typ: str, start_pin: int, end_pin: int) -> int:
SCAN_IDLE = 0x7f
SCAN_DONE_F = 0x80
@ -218,7 +218,7 @@ def jtag_scan(dev: DmjDevice, typ: str, start_pin: int, end_pin: int) -> int:
# ---
def sump_overclock_get(dev: DmjDevice) -> int:
def sump_overclock_get(dev: DPDevice) -> int:
try:
stat = dev.m4_sump_overclock_get()
print("SUMP overclocking mode: %d" % stat)
@ -228,7 +228,7 @@ def sump_overclock_get(dev: DmjDevice) -> int:
return 1
def sump_overclock_set(dev: DmjDevice, v: int) -> int:
def sump_overclock_set(dev: DPDevice, v: int) -> int:
try:
stat = dev.m4_sump_overclock_set(v)
return 0

View File

@ -179,7 +179,7 @@ class UsbConn(DevConn):
class ChardevConn(DevConn):
_DEVCLASSNAME = "dmj"
_DEVCLASSNAME = "dragonprobe"
def try_find() -> Optional[ChardevConn]:
if sys.platform != 'linux':

View File

@ -104,7 +104,7 @@ class ModeInfo(NamedTuple):
features: Set[int]
class DmjDevice:
class DPDevice:
def __init__(self, conn: DevConn):
self._conn = conn
self._buf = array.array('B')

View File

@ -160,7 +160,7 @@ static const struct i2c_algorithm usb_algorithm = {
static const struct usb_device_id i2c_tiny_usb_table[] = {
{ USB_DEVICE(0x0403, 0xc631) }, /* FTDI */
{ USB_DEVICE(0x1c40, 0x0534) }, /* EZPrototypes */
{ /* TinyUSB DapperMime: we want the Vendor interface on I2C-enabled ones */
{ /* Dragon Probe: we want the Vendor interface on I2C-enabled ones */
.match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION | USB_DEVICE_ID_MATCH_INT_CLASS,
.idVendor = 0xcafe, .idProduct = 0x1312,
.bcdDevice_lo = 0x6000, .bcdDevice_hi = 0x6fff,

8
host/modules/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
modules.order
Module.symvers
*.ko
*.mod
*.o
*.mod.c
.*.cmd
.*.d

View File

@ -1,5 +1,5 @@
obj-m := dmj.o dmj-char.o i2c-dmj.o spi-dmj.o dmj-hwmon.o
obj-m := dragonprobe.o dragonprobe-char.o i2c-dragonprobe.o spi-dragonprobe.o dragonprobe-hwmon.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
@ -9,18 +9,18 @@ default:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(RM) -v *.o *.ko *.mod *.mod.c Module.symvers modules.order
$(RM) -v *.o *.ko *.mod *.mod.c Module.symvers modules.order .*.cmd .*.d
load:
sudo insmod ./dmj.ko
sudo insmod ./dmj-char.ko
sudo insmod ./i2c-dmj.ko
sudo insmod ./spi-dmj.ko
sudo insmod ./dmj-hwmon.ko
sudo insmod ./dragonprobe.ko
sudo insmod ./dragonprobe-char.ko
sudo insmod ./i2c-dragonprobe.ko
sudo insmod ./spi-dragonprobe.ko
sudo insmod ./dragonprobe-hwmon.ko
unload:
sudo rmmod dmj-hwmon
sudo rmmod spi-dmj
sudo rmmod i2c-dmj
sudo rmmod dmj-char
sudo rmmod dmj
sudo rmmod dragonprobe-hwmon
sudo rmmod spi-dragonprobe
sudo rmmod i2c-dragonprobe
sudo rmmod dragonprobe-char
sudo rmmod dragonprobe

View File

@ -1,89 +0,0 @@
/* 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_RESP_STAT_ILLSTATE 0x05
#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)
#define DMJ_XFER_FLAGS_PARSE_RESP (1<<0)
#define DMJ_XFER_FLAGS_FILL_RECVBUF (1<<1)
inline static const char *dmj_get_protoerr(int err)
{
switch (err) {
case DMJ_RESP_STAT_OK: return "ok";
case DMJ_RESP_STAT_ILLCMD: return "unknown/unimplemented command";
case DMJ_RESP_STAT_BADMODE: return "bad mode";
case DMJ_RESP_STAT_NOSUCHMODE: return "no such mode available";
case DMJ_RESP_STAT_BADARG: return "illegal argument";
case DMJ_RESP_STAT_ILLSTATE: return "wrong state for command";
default: return "???";
}
}
inline static int dmj_check_retval(int ret, int len, struct device *dev,
const char *pfix, bool check_pos_val, int lmin, int lmax)
{
if (ret < 0) {
dev_err(dev, "%s: USB fail: %d\n", pfix, ret);
return ret;
}
if (ret && check_pos_val) {
dev_err(dev, "%s: USB protocol fail: %s (%d)\n", pfix, dmj_get_protoerr(ret), ret);
return -EIO;
}
if (len < lmin && lmin >= 0) {
dev_err(dev, "%s: USB reply too short: %d\n", pfix, len);
return -EREMOTEIO;
}
if (len > lmax && lmax >= 0) {
dev_err(dev, "%s: USB reply too long: %d\n", pfix, len);
return -EMSGSIZE;
}
return 0;
}
/* TODO: split up in "raw" read & writes, and higher-level ones with cmd and
* repstat, because the way this is currently overloaded, is, bad */
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

View File

@ -23,16 +23,16 @@
#include <linux/types.h>
#if 0
#include <linux/mfd/dmj.h>
#include <linux/mfd/dragonprobe.h>
#else
#include "dmj.h"
#include "dragonprobe.h"
#endif
#define HARDWARE_NAME "DapperMime-JTAG"
#define DEVICE_NAME "dmj-char"
#define CLASS_NAME "dmj"
#define HARDWARE_NAME "Dragon Probe"
#define DEVICE_NAME "dragonprobe-char"
#define CLASS_NAME "dragonprobe"
struct dmj_char_dev {
struct dp_char_dev {
struct cdev cdev;
struct device *dev;
struct platform_device *pdev;
@ -42,22 +42,22 @@ struct dmj_char_dev {
static int n_cdevs = 0;
static spinlock_t ndevs_lock;
static int dmj_char_major;
static struct class *dmj_char_class;
static int dp_char_major;
static struct class *dp_char_class;
static ssize_t dmj_char_read(struct file *file, char *buf, size_t len, loff_t *loff)
static ssize_t dp_char_read(struct file *file, char *buf, size_t len, loff_t *loff)
{
int res, ilen;
unsigned long ret;
struct dmj_char_dev *dmjch = file->private_data;
struct dp_char_dev *dpch = file->private_data;
void *kbuf = NULL;
struct device *dev = dmjch->dev;
struct device *dev = dpch->dev;
if (len > INT_MAX) return -EINVAL;
ilen = (int)len;
/* no flags: act like libusb read */
res = dmj_transfer(dmjch->pdev, -1, 0/*DMJ_XFER_FLAGS_FILL_RECVBUF*/, NULL, 0, &kbuf, &ilen);
res = dp_transfer(dpch->pdev, -1, 0/*DP_XFER_FLAGS_FILL_RECVBUF*/, NULL, 0, &kbuf, &ilen);
if (res < 0 || ilen < 0 || !kbuf) {
//dev_warn(dev, "err res=%d ilen=%d kbuf=%p\n", res, ilen, kbuf);
if (kbuf) kfree(kbuf);
@ -71,11 +71,11 @@ static ssize_t dmj_char_read(struct file *file, char *buf, size_t len, loff_t *l
return (ssize_t)ilen;
}
static ssize_t dmj_char_write(struct file *file, const char *buf, size_t len, loff_t *off)
static ssize_t dp_char_write(struct file *file, const char *buf, size_t len, loff_t *off)
{
unsigned long ret;
int res;
struct dmj_char_dev *dmjch = file->private_data;
struct dp_char_dev *dpch = file->private_data;
void *kbuf;
kbuf = kmalloc(len, GFP_KERNEL);
@ -87,47 +87,47 @@ static ssize_t dmj_char_write(struct file *file, const char *buf, size_t len, lo
return -EFAULT;
}
res = dmj_transfer(dmjch->pdev, -1, 0, kbuf, len, NULL, NULL);
res = dp_transfer(dpch->pdev, -1, 0, kbuf, len, NULL, NULL);
kfree(kbuf);
return (res < 0) ? res : len;
}
static int dmj_char_open(struct inode *inode, struct file *file)
static int dp_char_open(struct inode *inode, struct file *file)
{
struct dmj_char_dev *dmjch;
struct dp_char_dev *dpch;
dmjch = container_of(inode->i_cdev, struct dmj_char_dev, cdev);
dpch = container_of(inode->i_cdev, struct dp_char_dev, cdev);
file->private_data = dmjch;
file->private_data = dpch;
return 0;
}
static int dmj_char_release(struct inode *inode, struct file *file)
static int dp_char_release(struct inode *inode, struct file *file)
{
struct dmj_char_dev *dmjch;
struct dp_char_dev *dpch;
dmjch = container_of(inode->i_cdev, struct dmj_char_dev, cdev);
dpch = container_of(inode->i_cdev, struct dp_char_dev, cdev);
return 0;
}
static const struct file_operations dmj_char_fops = {
static const struct file_operations dp_char_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = dmj_char_read,
.write = dmj_char_write,
.read = dp_char_read,
.write = dp_char_write,
.unlocked_ioctl = NULL,
.open = dmj_char_open,
.release = dmj_char_release
.open = dp_char_open,
.release = dp_char_release
};
static int dmj_char_probe(struct platform_device *pdev)
static int dp_char_probe(struct platform_device *pdev)
{
int ret, minor;
struct device *device;
struct device *pd = &pdev->dev;
struct dmj_char_dev *dmjch;
struct dp_char_dev *dpch;
spin_lock(&ndevs_lock);
minor = n_cdevs;
@ -135,57 +135,57 @@ static int dmj_char_probe(struct platform_device *pdev)
spin_unlock(&ndevs_lock);
dev_info(pd, HARDWARE_NAME " /dev entries driver, major=%d, minor=%d\n",
dmj_char_major, minor);
dp_char_major, minor);
dmjch = devm_kzalloc(pd, sizeof(*dmjch), GFP_KERNEL);
if (!dmjch) return -ENOMEM;
dpch = devm_kzalloc(pd, sizeof(*dpch), GFP_KERNEL);
if (!dpch) return -ENOMEM;
platform_set_drvdata(pdev, dmjch);
platform_set_drvdata(pdev, dpch);
cdev_init(&dmjch->cdev, &dmj_char_fops);
ret = cdev_add(&dmjch->cdev, MKDEV(dmj_char_major, minor), 1);
cdev_init(&dpch->cdev, &dp_char_fops);
ret = cdev_add(&dpch->cdev, MKDEV(dp_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);
device = device_create(dp_char_class, pd, MKDEV(dp_char_major, minor), dpch, "dragonprobe-%d", minor);
if (IS_ERR(device)) {
ret = PTR_ERR(device);
dev_err(pd, "failed to create device: %d\n", ret);
cdev_del(&dmjch->cdev);
cdev_del(&dpch->cdev);
return ret;
}
dev_notice(device, "created device /dev/dmj-%d\n", minor);
dev_notice(device, "created device /dev/dragonprobe-%d\n", minor);
dmjch->dev = device;
dmjch->minor = minor;
dmjch->pdev = pdev;
dpch->dev = device;
dpch->minor = minor;
dpch->pdev = pdev;
return 0;
}
static int dmj_char_remove(struct platform_device *pdev)
static int dp_char_remove(struct platform_device *pdev)
{
struct dmj_char_dev *dmjch = platform_get_drvdata(pdev);
struct dp_char_dev *dpch = 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);
device_destroy(dp_char_class, MKDEV(dp_char_major, dpch->minor));
cdev_del(&dpch->cdev);
unregister_chrdev(MKDEV(dp_char_major, dpch->minor), CLASS_NAME);
return 0;
}
static struct platform_driver dmj_char_driver = {
static struct platform_driver dp_char_driver = {
.driver = {
.name = "dmj-char"
.name = "dragonprobe-char"
},
.probe = dmj_char_probe,
.remove = dmj_char_remove
.probe = dp_char_probe,
.remove = dp_char_remove
};
/*module_platform_driver(dmj_char_driver);*/
/*module_platform_driver(dp_char_driver);*/
static int __init dmj_char_init(void)
static int __init dp_char_init(void)
{
int ret, major;
struct class *class;
@ -213,33 +213,33 @@ static int __init dmj_char_init(void)
}
printk(KERN_DEBUG DEVICE_NAME " created class\n");
dmj_char_major = major;
dmj_char_class = class;
dp_char_major = major;
dp_char_class = class;
platform_driver_register(&dmj_char_driver);
platform_driver_register(&dp_char_driver);
return 0;
}
static void __exit dmj_char_exit(void)
static void __exit dp_char_exit(void)
{
platform_driver_unregister(&dmj_char_driver);
platform_driver_unregister(&dp_char_driver);
spin_lock(&ndevs_lock);
n_cdevs = 0;
spin_unlock(&ndevs_lock);
class_destroy(dmj_char_class);
unregister_chrdev(dmj_char_major, CLASS_NAME);
class_destroy(dp_char_class);
unregister_chrdev(dp_char_major, CLASS_NAME);
dmj_char_major = -1;
dmj_char_class = NULL;
dp_char_major = -1;
dp_char_class = NULL;
}
module_init(dmj_char_init);
module_exit(dmj_char_exit);
module_init(dp_char_init);
module_exit(dp_char_exit);
MODULE_AUTHOR("sys64738 <sys64738@disroot.org>");
MODULE_AUTHOR("haskal <haskal@awoo.systems>");
MODULE_DESCRIPTION("Character device for the " HARDWARE_NAME " USB multitool");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:dmj-char");
MODULE_ALIAS("platform:dragonprobe-char");

View File

@ -19,27 +19,27 @@
#include <linux/slab.h>
#if 0
#include <linux/mfd/dmj.h>
#include <linux/mfd/dragonprobe.h>
#else
#include "dmj.h"
#include "dragonprobe.h"
#endif
#define HARDWARE_NAME "DapperMime-JTAG"
#define HWMON_NAME "dmj"
#define HARDWARE_NAME "Dragon Probe"
#define HWMON_NAME "dragonprobe"
#define DMJ_TEMP_CMD_GET_ADDR 0x00
#define DMJ_TEMP_CMD_SET_ADDR 0x01
#define DMJ_TEMP_CMD_GET_TEMP 0x02
#define DMJ_TEMP_CMD_GET_MIN 0x03
#define DMJ_TEMP_CMD_GET_MAX 0x04
#define DMJ_TEMP_CMD_GET_CRIT 0x05
#define DP_TEMP_CMD_GET_ADDR 0x00
#define DP_TEMP_CMD_SET_ADDR 0x01
#define DP_TEMP_CMD_GET_TEMP 0x02
#define DP_TEMP_CMD_GET_MIN 0x03
#define DP_TEMP_CMD_GET_MAX 0x04
#define DP_TEMP_CMD_GET_CRIT 0x05
struct dmj_hwmon {
struct dp_hwmon {
struct platform_device *pdev;
struct device *hwmon_dev;
};
static umode_t dmj_hwmon_is_visible(const void *data, enum hwmon_sensor_types type,
static umode_t dp_hwmon_is_visible(const void *data, enum hwmon_sensor_types type,
uint32_t attr, int ch)
{
switch (attr) {
@ -53,10 +53,10 @@ static umode_t dmj_hwmon_is_visible(const void *data, enum hwmon_sensor_types ty
return 0;
}
}
static int dmj_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
static int dp_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
uint32_t attr, int ch, long *val)
{
struct dmj_hwmon *dmjh = dev_get_drvdata(dev);
struct dp_hwmon *dph = dev_get_drvdata(dev);
uint8_t subcmd, *rbuf;
uint16_t rval;
int ret, rlen;
@ -66,25 +66,25 @@ static int dmj_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
*val = 1;
return 0;
case hwmon_temp_input:
subcmd = DMJ_TEMP_CMD_GET_TEMP;
subcmd = DP_TEMP_CMD_GET_TEMP;
break;
case hwmon_temp_min:
subcmd = DMJ_TEMP_CMD_GET_MIN;
subcmd = DP_TEMP_CMD_GET_MIN;
break;
case hwmon_temp_max:
subcmd = DMJ_TEMP_CMD_GET_MAX;
subcmd = DP_TEMP_CMD_GET_MAX;
break;
case hwmon_temp_crit:
subcmd = DMJ_TEMP_CMD_GET_CRIT;
subcmd = DP_TEMP_CMD_GET_CRIT;
break;
default:
return -ENOTSUPP;
}
ret = dmj_transfer(dmjh->pdev, DMJ_CMD_MODE1_TEMPSENSOR,
DMJ_XFER_FLAGS_PARSE_RESP, &subcmd, sizeof(subcmd),
ret = dp_transfer(dph->pdev, DP_CMD_MODE1_TEMPSENSOR,
DP_XFER_FLAGS_PARSE_RESP, &subcmd, sizeof(subcmd),
(void**)&rbuf, &rlen);
ret = dmj_check_retval(ret, rlen, dev, "hwmon read", true, 2, 2);
ret = dp_check_retval(ret, rlen, dev, "hwmon read", true, 2, 2);
if (!ret) {
/* rval is 8.4 fixed point, bit 0x1000 is the sign bit, 0xe000 are flags */
rval = (uint16_t)rbuf[0] | ((uint16_t)rbuf[1] << 8);
@ -98,22 +98,22 @@ static int dmj_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
return ret;
}
static const struct hwmon_channel_info *dmj_hwmon_info[] = {
static const struct hwmon_channel_info *dp_hwmon_info[] = {
HWMON_CHANNEL_INFO(temp, HWMON_T_TYPE | HWMON_T_INPUT | HWMON_T_MIN |
HWMON_T_MAX | HWMON_T_CRIT),
NULL
};
static const struct hwmon_ops dmj_hwmon_ops = {
.is_visible = dmj_hwmon_is_visible,
.read = dmj_hwmon_read,
.write = NULL/*dmj_hwmon_write*/
static const struct hwmon_ops dp_hwmon_ops = {
.is_visible = dp_hwmon_is_visible,
.read = dp_hwmon_read,
.write = NULL/*dp_hwmon_write*/
};
static const struct hwmon_chip_info dmj_chip_info = {
.ops = &dmj_hwmon_ops,
.info = dmj_hwmon_info
static const struct hwmon_chip_info dp_chip_info = {
.ops = &dp_hwmon_ops,
.info = dp_hwmon_info
};
static int dmj_hwmon_check_hw(struct platform_device *pdev)
static int dp_hwmon_check_hw(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
uint16_t m1ver;
@ -122,9 +122,9 @@ static int dmj_hwmon_check_hw(struct platform_device *pdev)
int ret = 0, len;
uint8_t *buf = NULL;
ret = dmj_transfer(pdev, DMJ_CMD_CFG_GET_CUR_MODE,
DMJ_XFER_FLAGS_PARSE_RESP, NULL, 0, (void**)&buf, &len);
ret = dmj_check_retval(ret, len, dev, "hwmon test 1", true, sizeof(curmode), sizeof(curmode));
ret = dp_transfer(pdev, DP_CMD_CFG_GET_CUR_MODE,
DP_XFER_FLAGS_PARSE_RESP, NULL, 0, (void**)&buf, &len);
ret = dp_check_retval(ret, len, dev, "hwmon test 1", true, sizeof(curmode), sizeof(curmode));
if (ret < 0 || !buf) goto out;
curmode = buf[0];
@ -135,9 +135,9 @@ static int dmj_hwmon_check_hw(struct platform_device *pdev)
goto out;
}
ret = dmj_transfer(pdev, (1<<4) | DMJ_CMD_MODE_GET_VERSION,
DMJ_XFER_FLAGS_PARSE_RESP, NULL, 0, (void**)&buf, &len);
ret = dmj_check_retval(ret, len, dev, "hwmon test 2", true, sizeof(m1ver), sizeof(m1ver));
ret = dp_transfer(pdev, (1<<4) | DP_CMD_MODE_GET_VERSION,
DP_XFER_FLAGS_PARSE_RESP, NULL, 0, (void**)&buf, &len);
ret = dp_check_retval(ret, len, dev, "hwmon test 2", true, sizeof(m1ver), sizeof(m1ver));
if (ret < 0 || !buf) goto out;
m1ver = (uint16_t)buf[0] | ((uint16_t)buf[1] << 8);
@ -149,13 +149,13 @@ static int dmj_hwmon_check_hw(struct platform_device *pdev)
goto out;
}
ret = dmj_transfer(pdev, (1<<4) | DMJ_CMD_MODE_GET_FEATURES,
DMJ_XFER_FLAGS_PARSE_RESP, NULL, 0, (void**)&buf, &len);
ret = dmj_check_retval(ret, len, dev, "hwmon test 3", true, sizeof(m1feat), sizeof(m1feat));
ret = dp_transfer(pdev, (1<<4) | DP_CMD_MODE_GET_FEATURES,
DP_XFER_FLAGS_PARSE_RESP, NULL, 0, (void**)&buf, &len);
ret = dp_check_retval(ret, len, dev, "hwmon test 3", true, sizeof(m1feat), sizeof(m1feat));
if (ret < 0 || !buf) goto out;
m1feat = buf[0];
kfree(buf); buf = NULL;
if (!(m1feat & DMJ_FEATURE_MODE1_I2C)) {
if (!(m1feat & DP_FEATURE_MODE1_I2C)) {
dev_err(dev, "device's mode 1 does not support hwmon tempsensor\n");
ret = -EIO;
goto out;
@ -167,55 +167,55 @@ out:
return ret;
}
static int dmj_hwmon_probe(struct platform_device *pdev)
static int dp_hwmon_probe(struct platform_device *pdev)
{
struct dmj_hwmon *dmjh;
struct dp_hwmon *dph;
struct device *dev = &pdev->dev;
int ret;
ret = dmj_hwmon_check_hw(pdev);
ret = dp_hwmon_check_hw(pdev);
if (ret) {
dev_err(dev, "hw check failed: %d\n", ret);
return -ENODEV;
}
dmjh = devm_kzalloc(dev, sizeof(*dmjh), GFP_KERNEL);
if (!dmjh) return -ENOMEM;
dph = devm_kzalloc(dev, sizeof(*dph), GFP_KERNEL);
if (!dph) return -ENOMEM;
dmjh->pdev = pdev;
dph->pdev = pdev;
platform_set_drvdata(pdev, dmjh);
platform_set_drvdata(pdev, dph);
dmjh->hwmon_dev = hwmon_device_register_with_info(dev, HWMON_NAME, dmjh,
&dmj_chip_info, NULL);
if (IS_ERR(dmjh->hwmon_dev)) {
ret = PTR_ERR(dmjh->hwmon_dev);
dph->hwmon_dev = hwmon_device_register_with_info(dev, HWMON_NAME, dph,
&dp_chip_info, NULL);
if (IS_ERR(dph->hwmon_dev)) {
ret = PTR_ERR(dph->hwmon_dev);
dev_err(dev, "hwmon device registration failed\n");
}
return ret;
}
static int dmj_hwmon_remove(struct platform_device *pdev)
static int dp_hwmon_remove(struct platform_device *pdev)
{
struct dmj_hwmon *dmjh = platform_get_drvdata(pdev);
struct dp_hwmon *dph = platform_get_drvdata(pdev);
hwmon_device_unregister(dmjh->hwmon_dev);
hwmon_device_unregister(dph->hwmon_dev);
return 0;
}
static struct platform_driver dmj_hwmon_driver = {
static struct platform_driver dp_hwmon_driver = {
.driver = {
"dmj-hwmon",
"dragonprobe-hwmon",
},
.probe = dmj_hwmon_probe,
.remove = dmj_hwmon_remove
.probe = dp_hwmon_probe,
.remove = dp_hwmon_remove
};
module_platform_driver(dmj_hwmon_driver);
module_platform_driver(dp_hwmon_driver);
MODULE_AUTHOR("sys64738 <sys64738@disroot.org>");
MODULE_AUTHOR("haskal <haskal@awoo.systems>");
MODULE_DESCRIPTION("Hwmon driver for the " HARDWARE_NAME " USB multitool");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:dmj-hwmon");
MODULE_ALIAS("platform:dragonprobe-hwmon");

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for the DapperMime-JTAG USB multitool: base MFD driver
* Driver for the Dragon Probe USB multitool: base MFD driver
*
* Copyright (c) 2021 sys64738 and haskal
*
@ -20,23 +20,23 @@
#include <linux/rculist.h>
#if 0
#include <linux/mfd/dmj.h>
#include <linux/mfd/dragonprobe.h>
#else
#include "dmj.h"
#include "dragonprobe.h"
#endif
#define HARDWARE_NAME "DapperMime-JTAG"
#define HARDWARE_NAME_SYMBOLIC "dappermime-jtag"
#define HARDWARE_NAME "Dragon Probe"
#define HARDWARE_NAME_SYMBOLIC "dragonprobe"
#define DMJ_USB_TIMEOUT 500
#define DP_USB_TIMEOUT 500
#define DMJ_RESP_HDR_SIZE 4
#define DP_RESP_HDR_SIZE 4
/* endpoint indices, not addresses */
#define DMJ_VND_CFG_EP_OUT 0
#define DMJ_VND_CFG_EP_IN 1
#define DP_VND_CFG_EP_OUT 0
#define DP_VND_CFG_EP_IN 1
struct dmj_dev {
struct dp_dev {
struct usb_device *usb_dev;
struct usb_interface *interface;
uint8_t ep_in;
@ -45,12 +45,12 @@ struct dmj_dev {
spinlock_t disconnect_lock;
bool disconnect;
uint8_t dmj_mode, dmj_m1feature;
uint8_t dp_mode, dp_m1feature;
};
/* USB transfers */
static void *dmj_prep_buf(int cmd, const void *wbuf, int *wbufsize, gfp_t gfp)
static void *dp_prep_buf(int cmd, const void *wbuf, int *wbufsize, gfp_t gfp)
{
int len;
uint8_t *buf;
@ -79,41 +79,41 @@ static void *dmj_prep_buf(int cmd, const void *wbuf, int *wbufsize, gfp_t gfp)
return buf;
}
static int dmj_send_wait(struct dmj_dev *dmj, int cmd, const void *wbuf, int wbufsize)
static int dp_send_wait(struct dp_dev *dp, 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);
buf = dp_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);
ret = usb_bulk_msg(dp->usb_dev, usb_sndbulkpipe(dp->usb_dev, dp->ep_out),
buf, len, &actual, DP_USB_TIMEOUT);
kfree(buf);
return ret;
}
int dmj_xfer_internal(struct dmj_dev *dmj, int cmd, int recvflags,
int dp_xfer_internal(struct dp_dev *dp, int cmd, int recvflags,
const void *wbuf, int wbufsize, void **rbuf, int *rbufsize)
{
int ret = 0, actual, pl_off, todo;
struct device *dev = &dmj->interface->dev;
struct device *dev = &dp->interface->dev;
uint32_t pl_len;
void *tmpbuf = NULL, *longbuf = NULL;
uint8_t *bbuf;
uint8_t respstat;
spin_lock(&dmj->disconnect_lock);
if (dmj->disconnect) ret = -ENODEV;
spin_unlock(&dmj->disconnect_lock);
spin_lock(&dp->disconnect_lock);
if (dp->disconnect) ret = -ENODEV;
spin_unlock(&dp->disconnect_lock);
if (ret) return ret;
if ((cmd >= 0 && cmd <= 0xff) || (wbufsize && wbuf)) {
ret = dmj_send_wait(dmj, cmd, wbuf, wbufsize);
ret = dp_send_wait(dp, cmd, wbuf, wbufsize);
if (ret < 0) {
dev_err(dev, "USB write failed: %d\n", ret);
return ret;
@ -122,7 +122,7 @@ int dmj_xfer_internal(struct dmj_dev *dmj, int cmd, int recvflags,
if (rbuf) *rbuf = NULL;
if (recvflags & DMJ_XFER_FLAGS_PARSE_RESP) {
if (recvflags & DP_XFER_FLAGS_PARSE_RESP) {
/*
* if we do want to parse the response, we'll split the reads into
* blocks of 64 bytes, first to read the response header, and then
@ -136,8 +136,8 @@ int dmj_xfer_internal(struct dmj_dev *dmj, int cmd, int recvflags,
tmpbuf = kmalloc(64, GFP_KERNEL);
if (!tmpbuf) return -ENOMEM;
/* first read: 64b, with header data to parse */
ret = usb_bulk_msg(dmj->usb_dev, usb_rcvbulkpipe(dmj->usb_dev, dmj->ep_in),
tmpbuf, 64, &actual, DMJ_USB_TIMEOUT);
ret = usb_bulk_msg(dp->usb_dev, usb_rcvbulkpipe(dp->usb_dev, dp->ep_in),
tmpbuf, 64, &actual, DP_USB_TIMEOUT);
if (ret < 0) goto err_freetmp;
if (actual < 0) { ret = -EREMOTEIO; goto err_freetmp; }
@ -195,8 +195,8 @@ int dmj_xfer_internal(struct dmj_dev *dmj, int cmd, int recvflags,
actual = 64;
if (todo < actual) actual = todo;
ret = usb_bulk_msg(dmj->usb_dev, usb_rcvbulkpipe(dmj->usb_dev, dmj->ep_in),
tmpbuf, actual, &actual, DMJ_USB_TIMEOUT);
ret = usb_bulk_msg(dp->usb_dev, usb_rcvbulkpipe(dp->usb_dev, dp->ep_in),
tmpbuf, actual, &actual, DP_USB_TIMEOUT);
if (ret < 0) goto err_freelong;
if (actual < 0) { ret = -EREMOTEIO; goto err_freelong; }
if (actual > todo) {
@ -223,7 +223,7 @@ int dmj_xfer_internal(struct dmj_dev *dmj, int cmd, int recvflags,
} else {
/*
* otherwise, read max. rbufsize bytes (if using
* DMJ_XFER_FLAGS_FILL_RECVBUF, will try to fill it exactly, but it
* DP_XFER_FLAGS_FILL_RECVBUF, will try to fill it exactly, but it
* will error when going beyond!). also done in 64b chunks
*/
@ -232,7 +232,7 @@ int dmj_xfer_internal(struct dmj_dev *dmj, int cmd, int recvflags,
return 0;
}
if (recvflags & DMJ_XFER_FLAGS_FILL_RECVBUF) {
if (recvflags & DP_XFER_FLAGS_FILL_RECVBUF) {
tmpbuf = kmalloc(64, GFP_KERNEL);
if (!tmpbuf) return -ENOMEM;
longbuf = kmalloc(*rbufsize, GFP_KERNEL);
@ -244,8 +244,8 @@ int dmj_xfer_internal(struct dmj_dev *dmj, int cmd, int recvflags,
actual = 64;
if (todo < actual) actual = todo;
ret = usb_bulk_msg(dmj->usb_dev, usb_rcvbulkpipe(dmj->usb_dev, dmj->ep_in),
tmpbuf, actual, &actual, DMJ_USB_TIMEOUT);
ret = usb_bulk_msg(dp->usb_dev, usb_rcvbulkpipe(dp->usb_dev, dp->ep_in),
tmpbuf, actual, &actual, DP_USB_TIMEOUT);
if (ret < 0) goto err_freelong;
if (actual < 0) { ret = -EREMOTEIO; goto err_freelong; }
if (actual > todo) { ret = -EMSGSIZE; goto err_freelong; }
@ -265,8 +265,8 @@ int dmj_xfer_internal(struct dmj_dev *dmj, int cmd, int recvflags,
longbuf = kmalloc(*rbufsize, GFP_KERNEL);
if (!longbuf) return -ENOMEM;
ret = usb_bulk_msg(dmj->usb_dev, usb_rcvbulkpipe(dmj->usb_dev, dmj->ep_in),
longbuf, *rbufsize, rbufsize, DMJ_USB_TIMEOUT);
ret = usb_bulk_msg(dp->usb_dev, usb_rcvbulkpipe(dp->usb_dev, dp->ep_in),
longbuf, *rbufsize, rbufsize, DP_USB_TIMEOUT);
if (ret < 0) goto err_freelong;
if (*rbufsize < 0) {
//dev_warn(dev, "remoteio\n");
@ -290,37 +290,37 @@ err_freetmp:
return ret;
}
int dmj_transfer(struct platform_device *pdev, int cmd, int recvflags,
int dp_transfer(struct platform_device *pdev, int cmd, int recvflags,
const void *wbuf, int wbufsize, void **rbuf, int *rbufsize)
{
struct dmj_dev *dmj;
struct dp_dev *dp;
dmj = dev_get_drvdata(pdev->dev.parent);
dp = dev_get_drvdata(pdev->dev.parent);
return dmj_xfer_internal(dmj, cmd, recvflags, wbuf, wbufsize, rbuf, rbufsize);
return dp_xfer_internal(dp, cmd, recvflags, wbuf, wbufsize, rbuf, rbufsize);
}
EXPORT_SYMBOL(dmj_transfer);
EXPORT_SYMBOL(dp_transfer);
/* stuff on init */
static int dmj_check_hw(struct dmj_dev *dmj)
static int dp_check_hw(struct dp_dev *dp)
{
struct device *dev = &dmj->interface->dev;
struct device *dev = &dp->interface->dev;
int ret, len;
uint16_t protover;
uint8_t *buf = NULL;
ret = dmj_xfer_internal(dmj, DMJ_CMD_CFG_GET_VERSION,
DMJ_XFER_FLAGS_PARSE_RESP, NULL, 0, (void**)&buf, &len);
ret = dmj_check_retval(ret, len, dev, "version check", true, sizeof(protover), sizeof(protover));
ret = dp_xfer_internal(dp, DP_CMD_CFG_GET_VERSION,
DP_XFER_FLAGS_PARSE_RESP, NULL, 0, (void**)&buf, &len);
ret = dp_check_retval(ret, len, dev, "version check", true, sizeof(protover), sizeof(protover));
if (ret < 0 || !buf) goto out;
protover = (uint16_t)buf[0] | ((uint16_t)buf[1] << 8);
if (protover != DMJ_USB_CFG_PROTO_VER) {
dev_err(&dmj->interface->dev, HARDWARE_NAME " config protocol version 0x%04x too %s\n",
protover, (protover > DMJ_USB_CFG_PROTO_VER) ? "new" : "old");
if (protover != DP_USB_CFG_PROTO_VER) {
dev_err(&dp->interface->dev, HARDWARE_NAME " config protocol version 0x%04x too %s\n",
protover, (protover > DP_USB_CFG_PROTO_VER) ? "new" : "old");
ret = -ENODEV;
} else
@ -330,19 +330,19 @@ out:
if (buf) kfree(buf);
return ret;
}
static int dmj_print_info(struct dmj_dev *dmj)
static int dp_print_info(struct dp_dev *dp)
{
int ret, i, j, len;
uint16_t modes, mversion;
uint8_t curmode, features;
struct device *dev = &dmj->interface->dev;
struct device *dev = &dp->interface->dev;
uint8_t *buf;
char modeinfo[16], namebuf[64];
/* info string */
ret = dmj_xfer_internal(dmj, DMJ_CMD_CFG_GET_INFOSTR,
DMJ_XFER_FLAGS_PARSE_RESP, NULL, 0, (void**)&buf, &len);
ret = dmj_check_retval(ret, len, dev, "get info", true, -1, sizeof(namebuf)-1);
ret = dp_xfer_internal(dp, DP_CMD_CFG_GET_INFOSTR,
DP_XFER_FLAGS_PARSE_RESP, NULL, 0, (void**)&buf, &len);
ret = dp_check_retval(ret, len, dev, "get info", true, -1, sizeof(namebuf)-1);
if (ret < 0 || !buf) goto out;
memcpy(namebuf, buf, len);
namebuf[len] = 0;
@ -350,17 +350,17 @@ static int dmj_print_info(struct dmj_dev *dmj)
kfree(buf); buf = NULL;
/* cur mode */
ret = dmj_xfer_internal(dmj, DMJ_CMD_CFG_GET_CUR_MODE,
DMJ_XFER_FLAGS_PARSE_RESP, NULL, 0, (void**)&buf, &len);
ret = dmj_check_retval(ret, len, dev, "get info", true, sizeof(curmode), sizeof(curmode));
ret = dp_xfer_internal(dp, DP_CMD_CFG_GET_CUR_MODE,
DP_XFER_FLAGS_PARSE_RESP, NULL, 0, (void**)&buf, &len);
ret = dp_check_retval(ret, len, dev, "get info", true, sizeof(curmode), sizeof(curmode));
if (ret < 0 || !buf) goto out;
dmj->dmj_mode = curmode = buf[0];
dp->dp_mode = curmode = buf[0];
kfree(buf); buf = NULL;
/* map of available modes */
ret = dmj_xfer_internal(dmj, DMJ_CMD_CFG_GET_MODES,
DMJ_XFER_FLAGS_PARSE_RESP, NULL, 0, (void**)&buf, &len);
ret = dmj_check_retval(ret, len, dev, "get info", true, sizeof(modes), sizeof(modes));
ret = dp_xfer_internal(dp, DP_CMD_CFG_GET_MODES,
DP_XFER_FLAGS_PARSE_RESP, NULL, 0, (void**)&buf, &len);
ret = dp_check_retval(ret, len, dev, "get info", true, sizeof(modes), sizeof(modes));
if (ret < 0 || !buf) goto out;
modes = (uint16_t)buf[0] | ((uint16_t)buf[1] << 8);
kfree(buf); buf = NULL;
@ -379,31 +379,31 @@ static int dmj_print_info(struct dmj_dev *dmj)
if (!(modes & (1<<i))) continue; /* not available */
/* name */
ret = dmj_xfer_internal(dmj, (i<<4) | DMJ_CMD_MODE_GET_NAME,
DMJ_XFER_FLAGS_PARSE_RESP, NULL, 0, (void**)&buf, &len);
ret = dmj_check_retval(ret, len, dev, "get info", true, -1, sizeof(namebuf)-1);
ret = dp_xfer_internal(dp, (i<<4) | DP_CMD_MODE_GET_NAME,
DP_XFER_FLAGS_PARSE_RESP, NULL, 0, (void**)&buf, &len);
ret = dp_check_retval(ret, len, dev, "get info", true, -1, sizeof(namebuf)-1);
if (ret < 0 || !buf) goto out;
memcpy(namebuf, buf, len);
namebuf[len] = 0;
kfree(buf); buf = NULL;
/* version */
ret = dmj_xfer_internal(dmj, (i<<4) | DMJ_CMD_MODE_GET_VERSION,
DMJ_XFER_FLAGS_PARSE_RESP, NULL, 0, (void**)&buf, &len);
ret = dmj_check_retval(ret, len, dev, "get info", true, sizeof(mversion), sizeof(mversion));
ret = dp_xfer_internal(dp, (i<<4) | DP_CMD_MODE_GET_VERSION,
DP_XFER_FLAGS_PARSE_RESP, NULL, 0, (void**)&buf, &len);
ret = dp_check_retval(ret, len, dev, "get info", true, sizeof(mversion), sizeof(mversion));
if (ret < 0 || !buf) goto out;
mversion = (uint16_t)buf[0] | ((uint16_t)buf[1] << 8);
kfree(buf); buf = NULL;
/* features */
ret = dmj_xfer_internal(dmj, (i<<4) | DMJ_CMD_MODE_GET_FEATURES,
DMJ_XFER_FLAGS_PARSE_RESP, NULL, 0, (void**)&buf, &len);
ret = dmj_check_retval(ret, len, dev, "get info", true, sizeof(features), sizeof(features));
ret = dp_xfer_internal(dp, (i<<4) | DP_CMD_MODE_GET_FEATURES,
DP_XFER_FLAGS_PARSE_RESP, NULL, 0, (void**)&buf, &len);
ret = dp_check_retval(ret, len, dev, "get info", true, sizeof(features), sizeof(features));
if (ret < 0 || !buf) goto out;
features = (uint16_t)buf[0] | ((uint16_t)buf[1] << 8);
kfree(buf); buf = NULL;
if (i == 1) dmj->dmj_m1feature = features;
if (i == 1) dp->dp_m1feature = features;
for (j = 0; j < 8; ++j) {
if (features & (1<<j)) modeinfo[j] = '0'+j;
@ -421,39 +421,39 @@ out:
if (buf) kfree(buf);
return ret;
}
static int dmj_hw_init(struct dmj_dev *dmj)
static int dp_hw_init(struct dp_dev *dp)
{
int ret;
ret = dmj_check_hw(dmj);
ret = dp_check_hw(dp);
if (ret < 0) return ret;
return dmj_print_info(dmj);
return dp_print_info(dp);
}
/* MFD stuff */
static const struct mfd_cell dmj_mfd_char[] = {
{ .name = "dmj-char" },
static const struct mfd_cell dp_mfd_char[] = {
{ .name = "dragonprobe-char" },
};
static const struct mfd_cell dmj_mfd_spi[] = {
{ .name = "dmj-spi" },
static const struct mfd_cell dp_mfd_spi[] = {
{ .name = "dragonprobe-spi" },
};
static const struct mfd_cell dmj_mfd_i2c[] = {
{ .name = "dmj-i2c" },
static const struct mfd_cell dp_mfd_i2c[] = {
{ .name = "dragonprobe-i2c" },
};
static const struct mfd_cell dmj_mfd_hwmon[] = {
{ .name = "dmj-hwmon" },
static const struct mfd_cell dp_mfd_hwmon[] = {
{ .name = "dragonprobe-hwmon" },
};
/* USB device control */
static int dmj_probe(struct usb_interface *itf, const struct usb_device_id *usb_id)
static int dp_probe(struct usb_interface *itf, const struct usb_device_id *usb_id)
{
struct usb_host_interface *hostitf = itf->cur_altsetting;
struct usb_endpoint_descriptor *epin = NULL, *epout = NULL, *curep;
struct device *dev = &itf->dev;
struct dmj_dev *dmj;
struct dp_dev *dp;
int ret, i;
if (hostitf->desc.bNumEndpoints < 2) {
@ -479,46 +479,46 @@ static int dmj_probe(struct usb_interface *itf, const struct usb_device_id *usb_
return -ENODEV;
}
dmj = kzalloc(sizeof(*dmj), GFP_KERNEL);
if (!dmj) return -ENOMEM;
dp = kzalloc(sizeof(*dp), GFP_KERNEL);
if (!dp) return -ENOMEM;
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);
dp->ep_out = epout->bEndpointAddress;
dp->ep_in = epin->bEndpointAddress;
dp->usb_dev = usb_get_dev(interface_to_usbdev(itf));
dp->interface = itf;
usb_set_intfdata(itf, dp);
spin_lock_init(&dmj->disconnect_lock);
spin_lock_init(&dp->disconnect_lock);
ret = dmj_hw_init(dmj);
ret = dp_hw_init(dp);
if (ret < 0) {
dev_err(dev, "failed to initialize hardware\n");
goto out_free;
}
ret = mfd_add_hotplug_devices(dev, dmj_mfd_char, ARRAY_SIZE(dmj_mfd_char));
ret = mfd_add_hotplug_devices(dev, dp_mfd_char, ARRAY_SIZE(dp_mfd_char));
if (ret) {
dev_err(dev, "failed to add MFD character devices\n");
goto out_free;
}
if (dmj->dmj_mode == 1) {
if (dmj->dmj_m1feature & DMJ_FEATURE_MODE1_SPI) {
ret = mfd_add_hotplug_devices(dev, dmj_mfd_spi, ARRAY_SIZE(dmj_mfd_spi));
if (dp->dp_mode == 1) {
if (dp->dp_m1feature & DP_FEATURE_MODE1_SPI) {
ret = mfd_add_hotplug_devices(dev, dp_mfd_spi, ARRAY_SIZE(dp_mfd_spi));
if (ret) {
dev_err(dev, "failed to add MFD SPI devices\n");
goto out_free;
}
}
if (dmj->dmj_m1feature & DMJ_FEATURE_MODE1_I2C) {
ret = mfd_add_hotplug_devices(dev, dmj_mfd_i2c, ARRAY_SIZE(dmj_mfd_i2c));
if (dp->dp_m1feature & DP_FEATURE_MODE1_I2C) {
ret = mfd_add_hotplug_devices(dev, dp_mfd_i2c, ARRAY_SIZE(dp_mfd_i2c));
if (ret) {
dev_err(dev, "failed to add MFD I2C devices\n");
goto out_free;
}
}
if (dmj->dmj_m1feature & DMJ_FEATURE_MODE1_TEMPSENSOR) {
ret = mfd_add_hotplug_devices(dev, dmj_mfd_hwmon, ARRAY_SIZE(dmj_mfd_hwmon));
if (dp->dp_m1feature & DP_FEATURE_MODE1_TEMPSENSOR) {
ret = mfd_add_hotplug_devices(dev, dp_mfd_hwmon, ARRAY_SIZE(dp_mfd_hwmon));
if (ret) {
dev_err(dev, "failed to add MFD hwmon devices\n");
goto out_free;
@ -529,69 +529,69 @@ static int dmj_probe(struct usb_interface *itf, const struct usb_device_id *usb_
return 0;
out_free:
usb_put_dev(dmj->usb_dev);
kfree(dmj);
usb_put_dev(dp->usb_dev);
kfree(dp);
return ret;
}
static void dmj_disconnect(struct usb_interface *itf)
static void dp_disconnect(struct usb_interface *itf)
{
struct dmj_dev *dmj = usb_get_intfdata(itf);
struct dp_dev *dp = usb_get_intfdata(itf);
spin_lock(&dmj->disconnect_lock);
dmj->disconnect = true;
spin_unlock(&dmj->disconnect_lock);
spin_lock(&dp->disconnect_lock);
dp->disconnect = true;
spin_unlock(&dp->disconnect_lock);
mfd_remove_devices(&itf->dev);
usb_put_dev(dmj->usb_dev);
usb_put_dev(dp->usb_dev);
kfree(dmj);
kfree(dp);
}
static int dmj_suspend(struct usb_interface *itf, pm_message_t message)
static int dp_suspend(struct usb_interface *itf, pm_message_t message)
{
struct dmj_dev *dmj = usb_get_intfdata(itf);
struct dp_dev *dp = usb_get_intfdata(itf);
(void)message;
spin_lock(&dmj->disconnect_lock);
dmj->disconnect = true;
spin_unlock(&dmj->disconnect_lock);
spin_lock(&dp->disconnect_lock);
dp->disconnect = true;
spin_unlock(&dp->disconnect_lock);
return 0;
}
static int dmj_resume(struct usb_interface *itf)
static int dp_resume(struct usb_interface *itf)
{
struct dmj_dev *dmj = usb_get_intfdata(itf);
struct dp_dev *dp = usb_get_intfdata(itf);
dmj->disconnect = false;
dp->disconnect = false;
return 0;
}
static const struct usb_device_id dmj_table[] = {
static const struct usb_device_id dp_table[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(0xcafe, 0x1312, USB_CLASS_VENDOR_SPEC, 42, 69) },
{ }
};
MODULE_DEVICE_TABLE(usb, dmj_table);
MODULE_DEVICE_TABLE(usb, dp_table);
static struct usb_driver dmj_driver = {
static struct usb_driver dp_driver = {
.name = HARDWARE_NAME_SYMBOLIC,
.probe = dmj_probe,
.disconnect = dmj_disconnect,
.id_table = dmj_table,
.suspend = dmj_suspend,
.resume = dmj_resume,
.probe = dp_probe,
.disconnect = dp_disconnect,
.id_table = dp_table,
.suspend = dp_suspend,
.resume = dp_resume,
};
module_usb_driver(dmj_driver);
module_usb_driver(dp_driver);
MODULE_AUTHOR("sys64738 <sys64738@disroot.org>");
MODULE_AUTHOR("haskal <haskal@awoo.systems>");
MODULE_DESCRIPTION("Core driver for the " HARDWARE_NAME " USB multitool");
MODULE_LICENSE("GPL v2");
MODULE_SOFTDEP("post: dmj-char");
MODULE_SOFTDEP("post: i2c-dmj");
MODULE_SOFTDEP("post: spi-dmj");
MODULE_SOFTDEP("post: dmj-hwmon");
MODULE_SOFTDEP("post: dragonprobe-char");
MODULE_SOFTDEP("post: dragonprobe-i2c");
MODULE_SOFTDEP("post: dragonprobe-spi");
MODULE_SOFTDEP("post: dragonprobe-hwmon");

View File

@ -0,0 +1,89 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __LINUX_USB_DAPPERMIMEJTAG_H
#define __LINUX_USB_DAPPERMIMEJTAG_H
#define DP_USB_CFG_PROTO_VER 0x0010
#define DP_RESP_STAT_OK 0x00
#define DP_RESP_STAT_ILLCMD 0x01
#define DP_RESP_STAT_BADMODE 0x02
#define DP_RESP_STAT_NOSUCHMODE 0x03
#define DP_RESP_STAT_BADARG 0x04
#define DP_RESP_STAT_ILLSTATE 0x05
#define DP_CMD_CFG_GET_VERSION 0x00
#define DP_CMD_CFG_GET_MODES 0x01
#define DP_CMD_CFG_GET_CUR_MODE 0x02
#define DP_CMD_CFG_SET_CUR_MODE 0x03
#define DP_CMD_CFG_GET_INFOSTR 0x04
#define DP_CMD_MODE_GET_NAME 0x00
#define DP_CMD_MODE_GET_VERSION 0x01
#define DP_CMD_MODE_GET_FEATURES 0x02
#define DP_CMD_MODE1_SPI 0x13
#define DP_CMD_MODE1_I2C 0x14
#define DP_CMD_MODE1_TEMPSENSOR 0x15
#define DP_FEATURE_MODE1_UART (1<<0)
#define DP_FEATURE_MODE1_CMSISDAP (1<<1)
#define DP_FEATURE_MODE1_SPI (1<<2)
#define DP_FEATURE_MODE1_I2C (1<<3)
#define DP_FEATURE_MODE1_TEMPSENSOR (1<<4)
#define DP_XFER_FLAGS_PARSE_RESP (1<<0)
#define DP_XFER_FLAGS_FILL_RECVBUF (1<<1)
inline static const char *dp_get_protoerr(int err)
{
switch (err) {
case DP_RESP_STAT_OK: return "ok";
case DP_RESP_STAT_ILLCMD: return "unknown/unimplemented command";
case DP_RESP_STAT_BADMODE: return "bad mode";
case DP_RESP_STAT_NOSUCHMODE: return "no such mode available";
case DP_RESP_STAT_BADARG: return "illegal argument";
case DP_RESP_STAT_ILLSTATE: return "wrong state for command";
default: return "???";
}
}
inline static int dp_check_retval(int ret, int len, struct device *dev,
const char *pfix, bool check_pos_val, int lmin, int lmax)
{
if (ret < 0) {
dev_err(dev, "%s: USB fail: %d\n", pfix, ret);
return ret;
}
if (ret && check_pos_val) {
dev_err(dev, "%s: USB protocol fail: %s (%d)\n", pfix, dp_get_protoerr(ret), ret);
return -EIO;
}
if (len < lmin && lmin >= 0) {
dev_err(dev, "%s: USB reply too short: %d\n", pfix, len);
return -EREMOTEIO;
}
if (len > lmax && lmax >= 0) {
dev_err(dev, "%s: USB reply too long: %d\n", pfix, len);
return -EMSGSIZE;
}
return 0;
}
/* TODO: split up in "raw" read & writes, and higher-level ones with cmd and
* repstat, because the way this is currently overloaded, is, bad */
int dp_transfer(struct platform_device *pdev, int cmd, int recvflags,
const void *wbuf, int wbufsize, void **rbuf, int *rbufsize);
inline static int dp_read(struct platform_device *pdev, int recvflags,
void **rbuf, int *rbufsize)
{
return dp_transfer(pdev, -1, recvflags, NULL, 0, rbuf, rbufsize);
}
inline static int dp_write(struct platform_device *pdev, int cmd,
const void *wbuf, int wbufsize)
{
return dp_transfer(pdev, cmd, DP_XFER_FLAGS_PARSE_RESP, wbuf, wbufsize, NULL, NULL);
}
#endif

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for the DapperMime-JTAG USB multitool: USB-I2C adapter
* Driver for the Dragon Probe USB multitool: USB-I2C adapter
*
* Copyright (c) 2021 sys64738 and haskal
*
@ -18,42 +18,42 @@
#include <linux/acpi.h>
#if 0
#include <linux/mfd/dmj.h>
#include <linux/mfd/dragonprobe.h>
#else
#include "dmj.h"
#include "dragonprobe.h"
#endif
#define HARDWARE_NAME "DapperMime-JTAG"
#define HARDWARE_NAME "Dragon Probe"
#define DMJ_I2C_MAX_XFER_SIZE 64
#define DP_I2C_MAX_XFER_SIZE 64
#define DMJ_I2C_CMD_ECHO 0x00
#define DMJ_I2C_CMD_GET_FUNC 0x01
#define DMJ_I2C_CMD_SET_DELAY 0x02
#define DMJ_I2C_CMD_GET_STATUS 0x03
#define DMJ_I2C_CMD_DO_XFER 0x04
#define DMJ_I2C_CMD_DO_XFER_B 0x05
#define DMJ_I2C_CMD_DO_XFER_E 0x06
#define DMJ_I2C_CMD_DO_XFER_BE 0x07
#define DMJ_I2C_FLG_XFER_B 0x01
#define DMJ_I2C_FLG_XFER_E 0x02
#define DP_I2C_CMD_ECHO 0x00
#define DP_I2C_CMD_GET_FUNC 0x01
#define DP_I2C_CMD_SET_DELAY 0x02
#define DP_I2C_CMD_GET_STATUS 0x03
#define DP_I2C_CMD_DO_XFER 0x04
#define DP_I2C_CMD_DO_XFER_B 0x05
#define DP_I2C_CMD_DO_XFER_E 0x06
#define DP_I2C_CMD_DO_XFER_BE 0x07
#define DP_I2C_FLG_XFER_B 0x01
#define DP_I2C_FLG_XFER_E 0x02
#define DMJ_I2C_STAT_IDLE 0
#define DMJ_I2C_STAT_ACK 1
#define DMJ_I2C_STAT_NAK 2
#define DP_I2C_STAT_IDLE 0
#define DP_I2C_STAT_ACK 1
#define DP_I2C_STAT_NAK 2
static uint16_t delay = 10;
module_param(delay, ushort, 0);
MODULE_PARM_DESC(delay, "bit delay in microseconds (default is 10us for 100kHz)");
struct dmj_i2c {
struct dp_i2c {
struct platform_device *pdev;
struct i2c_adapter adapter;
};
static int dmj_i2c_read(struct dmj_i2c *dmji, struct i2c_msg *msg, int cmd)
static int dp_i2c_read(struct dp_i2c *dpi, struct i2c_msg *msg, int cmd)
{
struct device *dev = &dmji->pdev->dev;
struct device *dev = &dpi->pdev->dev;
void *respbuf = NULL;
int ret, len;
uint8_t cmdbuf[1+2+2+2]; /* cmd, flags, addr, len */
@ -66,9 +66,9 @@ static int dmj_i2c_read(struct dmj_i2c *dmji, struct i2c_msg *msg, int cmd)
cmdbuf[5] = (msg->len >> 0) & 0xff;
cmdbuf[6] = (msg->len >> 8) & 0xff;
ret = dmj_transfer(dmji->pdev, DMJ_CMD_MODE1_I2C, DMJ_XFER_FLAGS_PARSE_RESP,
ret = dp_transfer(dpi->pdev, DP_CMD_MODE1_I2C, DP_XFER_FLAGS_PARSE_RESP,
cmdbuf, sizeof(cmdbuf), &respbuf, &len);
ret = dmj_check_retval(ret, len, dev, "i2c read", true, -1, msg->len);
ret = dp_check_retval(ret, len, dev, "i2c read", true, -1, msg->len);
if (ret < 0 || !respbuf) goto err_free;
memcpy(msg->buf, respbuf, msg->len);
@ -78,9 +78,9 @@ err_free:
if (respbuf) kfree(respbuf);
return ret;
}
static int dmj_i2c_write(struct dmj_i2c *dmji, struct i2c_msg *msg, int cmd)
static int dp_i2c_write(struct dp_i2c *dpi, struct i2c_msg *msg, int cmd)
{
struct device *dev = &dmji->pdev->dev;
struct device *dev = &dpi->pdev->dev;
uint8_t *cmdbuf;
int ret, len;
@ -97,8 +97,8 @@ static int dmj_i2c_write(struct dmj_i2c *dmji, struct i2c_msg *msg, int cmd)
cmdbuf[6] = (msg->len >> 8) & 0xff;
memcpy(&cmdbuf[7], msg->buf, msg->len);
ret = dmj_write(dmji->pdev, DMJ_CMD_MODE1_I2C, cmdbuf, len);
ret = dmj_check_retval(ret, len, dev, "i2c write", true, -1, -1);
ret = dp_write(dpi->pdev, DP_CMD_MODE1_I2C, cmdbuf, len);
ret = dp_check_retval(ret, len, dev, "i2c write", true, -1, -1);
if (ret < 0) goto err_free;
ret = msg->len;
@ -108,18 +108,18 @@ err_free:
return ret;
}
static int dmj_i2c_xfer(struct i2c_adapter *a, struct i2c_msg *msgs, int nmsg)
static int dp_i2c_xfer(struct i2c_adapter *a, struct i2c_msg *msgs, int nmsg)
{
struct dmj_i2c *dmji = i2c_get_adapdata(a);
struct device *dev = &dmji->pdev->dev;
struct dp_i2c *dpi = i2c_get_adapdata(a);
struct device *dev = &dpi->pdev->dev;
struct i2c_msg *pmsg;
int i, ret, cmd, stlen;
uint8_t *status = NULL, i2ccmd;
for (i = 0; i < nmsg; ++i) {
cmd = DMJ_I2C_CMD_DO_XFER;
if (i == 0) cmd |= DMJ_I2C_FLG_XFER_B;
if (i == nmsg-1) cmd |= DMJ_I2C_FLG_XFER_E;
cmd = DP_I2C_CMD_DO_XFER;
if (i == 0) cmd |= DP_I2C_FLG_XFER_B;
if (i == nmsg-1) cmd |= DP_I2C_FLG_XFER_E;
pmsg = &msgs[i];
@ -129,7 +129,7 @@ static int dmj_i2c_xfer(struct i2c_adapter *a, struct i2c_msg *msgs, int nmsg)
pmsg->flags, pmsg->len, pmsg->addr);
if (pmsg->flags & I2C_M_RD) {
ret = dmj_i2c_read(dmji, pmsg, cmd);
ret = dp_i2c_read(dpi, pmsg, cmd);
if (ret < 0) goto err_ret;
if (ret != pmsg->len) {
dev_err(dev, "xfer rd: bad length %d vs %d\n", ret, pmsg->len);
@ -137,7 +137,7 @@ static int dmj_i2c_xfer(struct i2c_adapter *a, struct i2c_msg *msgs, int nmsg)
goto err_ret;
}
} else {
ret = dmj_i2c_write(dmji, pmsg, cmd);
ret = dp_i2c_write(dpi, pmsg, cmd);
if (ret < 0) goto err_ret;
if (ret != pmsg->len) {
dev_err(dev, "xfer wr: bad length %d vs %d\n", ret, pmsg->len);
@ -147,14 +147,14 @@ static int dmj_i2c_xfer(struct i2c_adapter *a, struct i2c_msg *msgs, int nmsg)
}
/* read status */
i2ccmd = DMJ_I2C_CMD_GET_STATUS;
ret = dmj_transfer(dmji->pdev, DMJ_CMD_MODE1_I2C, DMJ_XFER_FLAGS_PARSE_RESP,
i2ccmd = DP_I2C_CMD_GET_STATUS;
ret = dp_transfer(dpi->pdev, DP_CMD_MODE1_I2C, DP_XFER_FLAGS_PARSE_RESP,
&i2ccmd, sizeof(i2ccmd), (void**)&status, &stlen);
ret = dmj_check_retval(ret, stlen, dev, "i2c stat", true, sizeof(*status), sizeof(*status));
ret = dp_check_retval(ret, stlen, dev, "i2c stat", true, sizeof(*status), sizeof(*status));
if (ret < 0 || !status) goto err_ret;
dev_dbg(&a->dev, " status = %d\n", *status);
if (*status == DMJ_I2C_STAT_NAK) {
if (*status == DP_I2C_STAT_NAK) {
ret = -ENXIO;
goto err_ret;
}
@ -166,19 +166,19 @@ err_ret:
if (status) kfree(status);
return ret;
}
static uint32_t dmj_i2c_func(struct i2c_adapter *a)
static uint32_t dp_i2c_func(struct i2c_adapter *a)
{
struct dmj_i2c *dmji = i2c_get_adapdata(a);
struct device *dev = /*&dmji->pdev->dev;*/ &a->dev;
struct dp_i2c *dpi = i2c_get_adapdata(a);
struct device *dev = /*&dpi->pdev->dev;*/ &a->dev;
uint32_t func = 0;
int len, ret;
uint8_t i2ccmd = DMJ_I2C_CMD_GET_FUNC;
uint8_t i2ccmd = DP_I2C_CMD_GET_FUNC;
uint8_t *fbuf = NULL;
ret = dmj_transfer(dmji->pdev, DMJ_CMD_MODE1_I2C, DMJ_XFER_FLAGS_PARSE_RESP,
ret = dp_transfer(dpi->pdev, DP_CMD_MODE1_I2C, DP_XFER_FLAGS_PARSE_RESP,
&i2ccmd, sizeof(i2ccmd), (void**)&fbuf, &len);
ret = dmj_check_retval(ret, len, dev, "i2c get_func", true, sizeof(func), sizeof(func));
ret = dp_check_retval(ret, len, dev, "i2c get_func", true, sizeof(func), sizeof(func));
if (ret < 0 || !fbuf) return 0;
func = (uint32_t)fbuf[0] | ((uint32_t)fbuf[1] << 8)
@ -190,16 +190,16 @@ static uint32_t dmj_i2c_func(struct i2c_adapter *a)
return func;
}
static const struct i2c_algorithm dmj_i2c_algo = {
.master_xfer = dmj_i2c_xfer,
.functionality = dmj_i2c_func
static const struct i2c_algorithm dp_i2c_algo = {
.master_xfer = dp_i2c_xfer,
.functionality = dp_i2c_func
};
static const struct i2c_adapter_quirks dmj_i2c_quirks = {
.max_read_len = DMJ_I2C_MAX_XFER_SIZE,
.max_write_len = DMJ_I2C_MAX_XFER_SIZE,
static const struct i2c_adapter_quirks dp_i2c_quirks = {
.max_read_len = DP_I2C_MAX_XFER_SIZE,
.max_write_len = DP_I2C_MAX_XFER_SIZE,
};
static int dmj_i2c_check_hw(struct platform_device *pdev)
static int dp_i2c_check_hw(struct platform_device *pdev)
{
/*
* 1. check if mode 1 is available
@ -216,9 +216,9 @@ static int dmj_i2c_check_hw(struct platform_device *pdev)
int ret = 0, len;
uint8_t *buf = NULL;
ret = dmj_transfer(pdev, DMJ_CMD_CFG_GET_CUR_MODE,
DMJ_XFER_FLAGS_PARSE_RESP, NULL, 0, (void**)&buf, &len);
ret = dmj_check_retval(ret, len, dev, "i2c test 1", true, sizeof(curmode), sizeof(curmode));
ret = dp_transfer(pdev, DP_CMD_CFG_GET_CUR_MODE,
DP_XFER_FLAGS_PARSE_RESP, NULL, 0, (void**)&buf, &len);
ret = dp_check_retval(ret, len, dev, "i2c test 1", true, sizeof(curmode), sizeof(curmode));
if (ret < 0 || !buf) goto out;
curmode = buf[0];
@ -229,9 +229,9 @@ static int dmj_i2c_check_hw(struct platform_device *pdev)
goto out;
}
ret = dmj_transfer(pdev, (1<<4) | DMJ_CMD_MODE_GET_VERSION,
DMJ_XFER_FLAGS_PARSE_RESP, NULL, 0, (void**)&buf, &len);
ret = dmj_check_retval(ret, len, dev, "i2c test 2", true, sizeof(m1ver), sizeof(m1ver));
ret = dp_transfer(pdev, (1<<4) | DP_CMD_MODE_GET_VERSION,
DP_XFER_FLAGS_PARSE_RESP, NULL, 0, (void**)&buf, &len);
ret = dp_check_retval(ret, len, dev, "i2c test 2", true, sizeof(m1ver), sizeof(m1ver));
if (ret < 0 || !buf) goto out;
m1ver = (uint16_t)buf[0] | ((uint16_t)buf[1] << 8);
@ -243,24 +243,24 @@ static int dmj_i2c_check_hw(struct platform_device *pdev)
goto out;
}
ret = dmj_transfer(pdev, (1<<4) | DMJ_CMD_MODE_GET_FEATURES,
DMJ_XFER_FLAGS_PARSE_RESP, NULL, 0, (void**)&buf, &len);
ret = dmj_check_retval(ret, len, dev, "i2c test 3", true, sizeof(m1feat), sizeof(m1feat));
ret = dp_transfer(pdev, (1<<4) | DP_CMD_MODE_GET_FEATURES,
DP_XFER_FLAGS_PARSE_RESP, NULL, 0, (void**)&buf, &len);
ret = dp_check_retval(ret, len, dev, "i2c test 3", true, sizeof(m1feat), sizeof(m1feat));
if (ret < 0 || !buf) goto out;
m1feat = buf[0];
kfree(buf); buf = NULL;
if (!(m1feat & DMJ_FEATURE_MODE1_I2C)) {
if (!(m1feat & DP_FEATURE_MODE1_I2C)) {
dev_err(dev, "device's mode 1 does not support I2C\n");
ret = -EIO;
goto out;
}
echoval = 0x42;
i2ccmd[0] = DMJ_I2C_CMD_ECHO;
i2ccmd[0] = DP_I2C_CMD_ECHO;
i2ccmd[1] = ~echoval;
ret = dmj_transfer(pdev, (1<<4) | DMJ_CMD_MODE1_I2C,
DMJ_XFER_FLAGS_PARSE_RESP, i2ccmd, sizeof(i2ccmd), (void**)&buf, &len);
ret = dmj_check_retval(ret, len, dev, "i2c test", true, sizeof(echoval), sizeof(echoval));
ret = dp_transfer(pdev, (1<<4) | DP_CMD_MODE1_I2C,
DP_XFER_FLAGS_PARSE_RESP, i2ccmd, sizeof(i2ccmd), (void**)&buf, &len);
ret = dp_check_retval(ret, len, dev, "i2c test", true, sizeof(echoval), sizeof(echoval));
if (ret < 0 || !buf) goto out;
echoval = buf[0];
@ -278,94 +278,94 @@ out:
return ret;
}
static int dmj_i2c_set_delay(struct platform_device *pdev, uint16_t us)
static int dp_i2c_set_delay(struct platform_device *pdev, uint16_t us)
{
struct device *dev = &pdev->dev;
uint8_t i2ccmd[3];
int ret = 0;
i2ccmd[0] = DMJ_I2C_CMD_SET_DELAY;
i2ccmd[0] = DP_I2C_CMD_SET_DELAY;
i2ccmd[1] = (us >> 0) & 0xff;
i2ccmd[2] = (us >> 8) & 0xff;
ret = dmj_write(pdev, (1<<4) | DMJ_CMD_MODE1_I2C, i2ccmd, sizeof(i2ccmd));
ret = dp_write(pdev, (1<<4) | DP_CMD_MODE1_I2C, i2ccmd, sizeof(i2ccmd));
dev_dbg(dev, "set delay to %hu us, result %d\n", us, ret);
ret = dmj_check_retval(ret, -1, dev, "i2c set delay", true, -1, -1);
ret = dp_check_retval(ret, -1, dev, "i2c set delay", true, -1, -1);
return ret;
}
static int dmj_i2c_probe(struct platform_device *pdev)
static int dp_i2c_probe(struct platform_device *pdev)
{
int ret, hwnlen;
struct dmj_i2c *dmji;
struct dp_i2c *dpi;
struct device *dev = &pdev->dev;
void *hwname;
char namebuf[64];
ret = dmj_i2c_check_hw(pdev);
ret = dp_i2c_check_hw(pdev);
if (ret) return -ENODEV;
ret = dmj_i2c_set_delay(pdev, delay);
ret = dp_i2c_set_delay(pdev, delay);
if (ret) {
dev_err(dev, "failed to set I2C speed: %d\n", ret);
return ret;
}
dmji = devm_kzalloc(dev, sizeof(*dmji), GFP_KERNEL);
if (!dmji) return -ENOMEM;
dpi = devm_kzalloc(dev, sizeof(*dpi), GFP_KERNEL);
if (!dpi) return -ENOMEM;
dmji->pdev = pdev;
dpi->pdev = pdev;
dmji->adapter.owner = THIS_MODULE;
dmji->adapter.class = I2C_CLASS_HWMON;
dmji->adapter.algo = &dmj_i2c_algo;
dmji->adapter.quirks = &dmj_i2c_quirks; /* TODO: is this needed? probably... */
dmji->adapter.dev.of_node = dev->of_node;
i2c_set_adapdata(&dmji->adapter, dmji);
dpi->adapter.owner = THIS_MODULE;
dpi->adapter.class = I2C_CLASS_HWMON;
dpi->adapter.algo = &dp_i2c_algo;
dpi->adapter.quirks = &dp_i2c_quirks; /* TODO: is this needed? probably... */
dpi->adapter.dev.of_node = dev->of_node;
i2c_set_adapdata(&dpi->adapter, dpi);
/* get device name, for adapter name */
ret = dmj_transfer(pdev, DMJ_CMD_CFG_GET_INFOSTR,
DMJ_XFER_FLAGS_PARSE_RESP, NULL, 0, (void**)&hwname, &hwnlen);
ret = dmj_check_retval(ret, hwnlen, dev, "probe: get name", true, -1, sizeof(namebuf)-1);
ret = dp_transfer(pdev, DP_CMD_CFG_GET_INFOSTR,
DP_XFER_FLAGS_PARSE_RESP, NULL, 0, (void**)&hwname, &hwnlen);
ret = dp_check_retval(ret, hwnlen, dev, "probe: get name", true, -1, sizeof(namebuf)-1);
if (ret < 0 || !hwname) return -EIO;
memcpy(namebuf, hwname, hwnlen);
namebuf[hwnlen] = 0;
kfree(hwname);
snprintf(dmji->adapter.name, sizeof(dmji->adapter.name),
snprintf(dpi->adapter.name, sizeof(dpi->adapter.name),
HARDWARE_NAME " '%s' at %s", namebuf, dev_name(pdev->dev.parent));
platform_set_drvdata(pdev, dmji);
platform_set_drvdata(pdev, dpi);
ret = i2c_add_adapter(&dmji->adapter);
ret = i2c_add_adapter(&dpi->adapter);
if (!ret) {
dev_info(dev, HARDWARE_NAME " I2C device driver at i2c-%d, %s\n",
dmji->adapter.nr, dmji->adapter.name);
dpi->adapter.nr, dpi->adapter.name);
}
return ret;
}
static int dmj_i2c_remove(struct platform_device *pdev)
static int dp_i2c_remove(struct platform_device *pdev)
{
struct dmj_i2c *dmji = platform_get_drvdata(pdev);
struct dp_i2c *dpi = platform_get_drvdata(pdev);
i2c_del_adapter(&dmji->adapter);
i2c_del_adapter(&dpi->adapter);
return 0;
}
static struct platform_driver dmj_i2c_drv = {
.driver.name = "dmj-i2c",
.probe = dmj_i2c_probe,
.remove = dmj_i2c_remove
static struct platform_driver dp_i2c_drv = {
.driver.name = "dragonprobe-i2c",
.probe = dp_i2c_probe,
.remove = dp_i2c_remove
};
module_platform_driver(dmj_i2c_drv);
module_platform_driver(dp_i2c_drv);
MODULE_AUTHOR("sys64738 <sys64738@disroot.org>");
MODULE_AUTHOR("haskal <haskal@awoo.systems>");
MODULE_DESCRIPTION("I2C interface driver for the " HARDWARE_NAME " USB multitool");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:dmj-i2c");
MODULE_ALIAS("platform:dragonprobe-i2c");

View File

@ -1,984 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for the DapperMime-JTAG USB multitool: USB-SPI adapter
*
* Copyright (c) 2021 sys64738 and haskal
*
* Adapted from:
* spi-dln2.c, Copyright (c) 2014 Intel Corporation
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/pm_runtime.h>
#include <asm/unaligned.h>
#if 0
#include <linux/mfd/dmj.h>
#else
#include "dmj.h"
#endif
#define HARDWARE_NAME "DapperMime-JTAG"
#define DMJ_SPI_CMD_NOP 0x00
#define DMJ_SPI_CMD_Q_IFACE 0x01
#define DMJ_SPI_CMD_Q_CMDMAP 0x02
#define DMJ_SPI_CMD_Q_PGMNAME 0x03
#define DMJ_SPI_CMD_Q_SERBUF 0x04
#define DMJ_SPI_CMD_Q_BUSTYPE 0x05
#define DMJ_SPI_CMD_Q_CHIPSIZE 0x06
#define DMJ_SPI_CMD_Q_OPBUF 0x07
#define DMJ_SPI_CMD_Q_WRNMAXLEN 0x08
#define DMJ_SPI_CMD_R_BYTE 0x09
#define DMJ_SPI_CMD_R_NBYTES 0x0a
#define DMJ_SPI_CMD_O_INIT 0x0b
#define DMJ_SPI_CMD_O_WRITEB 0x0c
#define DMJ_SPI_CMD_O_WRITEN 0x0d
#define DMJ_SPI_CMD_O_DELAY 0x0e
#define DMJ_SPI_CMD_O_EXEC 0x0f
#define DMJ_SPI_CMD_SYNCNOP 0x10
#define DMJ_SPI_CMD_Q_RDNMAXLEN 0x11
#define DMJ_SPI_CMD_S_BUSTYPE 0x12
#define DMJ_SPI_CMD_SPIOP 0x13
#define DMJ_SPI_CMD_S_SPI_FREQ 0x14
#define DMJ_SPI_CMD_S_PINSTATE 0x15
#define DMJ_SPI_CMD_Q_SPI_CAPS 0x40
#define DMJ_SPI_CMD_S_SPI_CHIPN 0x41
#define DMJ_SPI_CMD_S_SPI_SETCS 0x42
#define DMJ_SPI_CMD_S_SPI_FLAGS 0x43
#define DMJ_SPI_CMD_S_SPI_BPW 0x44
#define DMJ_SPI_CMD_SPI_READ 0x45
#define DMJ_SPI_CMD_SPI_WRITE 0x46
#define DMJ_SPI_CMD_SPI_RDWR 0x47
#define SERPROG_IFACE_VERSION 0x0001
static const uint8_t reqd_cmds[] = {
DMJ_SPI_CMD_NOP, DMJ_SPI_CMD_Q_IFACE, DMJ_SPI_CMD_Q_CMDMAP,
/*DMJ_SPI_CMD_Q_WRNMAXLEN, DMJ_SPI_CMD_Q_RDNMAXLEN,*/
DMJ_SPI_CMD_S_SPI_FREQ, /*DMJ_SPI_CMD_S_PINSTATE,*/ /*DMJ_SPI_CMD_SPIOP,*/
DMJ_SPI_CMD_Q_SPI_CAPS, DMJ_SPI_CMD_S_SPI_CHIPN, DMJ_SPI_CMD_S_SPI_FLAGS,
DMJ_SPI_CMD_S_SPI_BPW, DMJ_SPI_CMD_S_SPI_SETCS,
/*DMJ_SPI_CMD_SPI_READ, DMJ_SPI_CMD_SPI_WRITE, DMJ_SPI_CMD_SPI_RDWR,*/
};
#define DMJ_SPI_ACK 0x06
#define DMJ_SPI_NAK 0x15
#define DMJ_SPI_S_FLG_CPHA (1<<0)
#define DMJ_SPI_S_FLG_CPOL (1<<1)
#define DMJ_SPI_S_FLG_STDSPI (0<<2)
#define DMJ_SPI_S_FLG_TISSP (1<<2)
#define DMJ_SPI_S_FLG_MICROW (2<<2)
#define DMJ_SPI_S_FLG_MSBFST (0<<4)
#define DMJ_SPI_S_FLG_LSBFST (1<<4)
#define DMJ_SPI_S_FLG_CSACLO (0<<5)
#define DMJ_SPI_S_FLG_CSACHI (1<<5)
#define DMJ_SPI_S_FLG_3WIRE (1<<6)
#define DMJ_SPI_S_CAP_CPHA_HI (1<<0)
#define DMJ_SPI_S_CAP_CPHA_LO (1<<1)
#define DMJ_SPI_S_CAP_CPOL_HI (1<<2)
#define DMJ_SPI_S_CAP_CPOL_LO (1<<3)
#define DMJ_SPI_S_CAP_STDSPI (1<<4)
#define DMJ_SPI_S_CAP_TISSP (1<<5)
#define DMJ_SPI_S_CAP_MICROW (1<<6)
#define DMJ_SPI_S_CAP_MSBFST (1<<7)
#define DMJ_SPI_S_CAP_LSBFST (1<<8)
#define DMJ_SPI_S_CAP_CSACHI (1<<9)
#define DMJ_SPI_S_CAP_3WIRE (1<<10)
#define DMJ_PINST_AUTOSUSPEND_TIMEOUT 2000
struct dmj_spi_caps {
uint32_t freq_min, freq_max;
uint16_t flgcaps;
uint8_t num_cs, min_bpw, max_bpw;
};
struct dmj_spi_dev_sett {
/* does not have to be guarded with a spinlock, as the kernel already
* serializes transfer_one/set_cs calls */
uint32_t freq;
uint8_t flags, bpw;
uint8_t cs, pinst;
};
struct dmj_spi {
struct platform_device *pdev;
struct spi_controller *spictl;
uint8_t *txbuf;
struct dmj_spi_caps caps;
uint8_t csmask;
struct dmj_spi_dev_sett devsettings[8];
uint32_t wrnmaxlen, rdnmaxlen;
uint8_t cmdmap[32];
spinlock_t csmap_lock;
struct spi_board_info binfo[8];
};
static int dmj_check_retval_sp(int ret, int len, struct device *dev,
const char *pfix, bool check_pos_val, int lmin, int lmax, const void* rbuf)
{
ret = dmj_check_retval(ret, len, dev, pfix, check_pos_val, lmin, lmax);
if (ret >= 0 && ((const uint8_t *)rbuf)[0] != DMJ_SPI_ACK) {
dev_err(dev, "%s: did not receive ACK\n", pfix);
ret = -EIO;
}
return ret;
}
static bool has_cmd(struct dmj_spi *dmjs, int cmd)
{
int byteind = cmd >> 3, bitind = cmd & 7;
return dmjs->cmdmap[byteind] & (1 << bitind);
}
static uint8_t kernmode_to_flags(uint16_t caps, int mode)
{
uint8_t ret = mode & 3; /* bottom 2 bits are the SPI mode (CPHA & CPOL) */
if (mode & SPI_LSB_FIRST) ret |= DMJ_SPI_S_FLG_LSBFST;
else ret |= DMJ_SPI_S_FLG_MSBFST;
if (mode & SPI_CS_HIGH) ret |= DMJ_SPI_S_FLG_CSACHI;
else ret |= DMJ_SPI_S_FLG_CSACLO;
if (mode & SPI_3WIRE) ret |= DMJ_SPI_S_FLG_3WIRE;
/* need some defaults for other stuff */
if (caps & DMJ_SPI_S_CAP_STDSPI) ret |= DMJ_SPI_S_FLG_STDSPI;
else if (caps & DMJ_SPI_S_CAP_TISSP) ret |= DMJ_SPI_S_FLG_TISSP;
else if (caps & DMJ_SPI_S_CAP_MICROW) ret |= DMJ_SPI_S_FLG_MICROW;
else ret |= DMJ_SPI_S_FLG_STDSPI; /* shrug, also shouldn't happen (cf. get_caps) */
return ret;
}
static int devcaps_to_kernmode(uint16_t caps)
{
int ret;
ret = caps & 3; /* SPI mode (CPHA & CPOL) bits */
if (caps & DMJ_SPI_S_CAP_LSBFST) ret |= SPI_LSB_FIRST;
if (caps & DMJ_SPI_S_CAP_CSACHI) ret |= SPI_CS_HIGH;
if (caps & DMJ_SPI_S_CAP_3WIRE) ret |= SPI_3WIRE;
return ret;
}
static void caps_to_binfo(struct dmj_spi *dmjs, int busnum)
{
int i;
for (i = 0; i < dmjs->caps.num_cs; ++i) {
snprintf(dmjs->binfo[i].modalias, SPI_NAME_SIZE, "spidev");
dmjs->binfo[i].controller_data = dmjs;
dmjs->binfo[i].max_speed_hz = dmjs->caps.freq_max;
dmjs->binfo[i].bus_num = busnum;
dmjs->binfo[i].chip_select = i;
dmjs->binfo[i].mode = 0; /* shrug */
}
}
static void bufconv_to_le(void *dst, const void *src, size_t len_bytes, uint8_t bpw)
{
#ifdef __LITTLE_ENDIAN
memcpy(dst, src, len_bytes);
#else
if (bpw > 16) {
__le32 *dst32 = (__le32 *)dst;
const uint32_t *src32 = (const uint32_t *)src;
for (size_t i = 0; i < len_bytes; i += 4, ++dst32, ++src32) {
*dst32 = cpu_to_le32p(src32);
}
} else if (bpw > 8) {
__le16 *dst16 = (__le16 *)dst;
const uint16_t *src16 = (const uint16_t *)src;
for (size_t i = 0; i < len_bytes; i += 2, ++dst16, ++src16) {
*dst16 = cpu_to_le16p(src16);
}
} else {
memcpy(dst, src, len_bytes);
}
#endif
}
static void bufconv_from_le(void *dst, const void *src, size_t len_bytes, uint8_t bpw)
{
#ifdef __LITTLE_ENDIAN
memcpy(dst, src, len_bytes);
#else
if (bpw > 16) {
const __le32 *src32 = (const __le32 *)src;
uint32_t *dst32 = (uint32_t *)dst;
for (size_t i = 0; i < len_bytes; i += 4, ++dst32, ++src32) {
*dst32 = get_unaligned_le32(src32);
}
} else if (bpw > 8) {
const __le16 *src16 = (const __le16 *)src;
uint16_t *dst16 = (uint16_t *)dst;
for (size_t i = 0; i < len_bytes; i += 2, ++dst16, ++src16) {
*dst16 = le16_to_cpup(src16);
}
} else {
memcpy(dst, src, len_bytes);
}
#endif
}
static int dmj_spi_csmask_set(struct dmj_spi *dmjs, uint8_t csmask)
{
struct device *dev = &dmjs->pdev->dev;
uint8_t oldcm;
bool do_csmask = false;
uint8_t wbuf[] = { DMJ_SPI_CMD_S_SPI_CHIPN, csmask };
uint8_t *rbuf;
int ret, rlen;
spin_lock(&dmjs->csmap_lock);
oldcm = dmjs->csmask;
if (oldcm != csmask) {
dmjs->csmask = csmask;
do_csmask = true;
}
spin_unlock(&dmjs->csmap_lock);
if (do_csmask) {
dev_dbg(dev, "set csmask %02x\n", csmask);
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
ret = dmj_check_retval_sp(ret, rlen, dev, "set CS mask", true, 1, 1, rbuf);
if (rbuf) kfree(rbuf);
}
return 0;
}
static int dmj_spi_csmask_set_one(struct dmj_spi *dmjs, uint8_t cs)
{
return dmj_spi_csmask_set(dmjs, BIT(cs));
}
static int dmj_spi_cs_set(struct dmj_spi *dmjs, int ind, bool lvl)
{
struct device *dev = &dmjs->pdev->dev;
uint8_t wbuf[] = { DMJ_SPI_CMD_S_SPI_SETCS, lvl ? 1 : 0 };
uint8_t *rbuf;
int ret, rlen;
if (dmjs->devsettings[ind].cs == (lvl ? 1 : 0)) return 0;
dev_dbg(dev, "set cs %s\n", lvl?"hi":"lo");
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
ret = dmj_check_retval_sp(ret, rlen, dev, "set CS", true, 1, 1, rbuf);
if (!ret) {
dmjs->devsettings[ind].cs = lvl ? 1 : 0;
}
if (rbuf) kfree(rbuf);
return ret;
}
static int dmj_spi_get_caps(struct dmj_spi *dmjs)
{
struct device *dev = &dmjs->pdev->dev;
uint8_t wbuf[] = { DMJ_SPI_CMD_Q_SPI_CAPS };
uint8_t *rbuf;
int ret, rlen;
dev_dbg(dev, "get caps\n");
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
ret = dmj_check_retval_sp(ret, rlen, dev, "get caps", true, 14, 14, rbuf);
if (!ret) {
dmjs->caps.freq_min = (uint32_t)rbuf[1] | ((uint32_t)rbuf[2] << 8)
| ((uint32_t)rbuf[3] << 16) | ((uint32_t)rbuf[4] << 24);
dmjs->caps.freq_max = (uint32_t)rbuf[5] | ((uint32_t)rbuf[6] << 8)
| ((uint32_t)rbuf[7] << 16) | ((uint32_t)rbuf[8] << 24);
dmjs->caps.flgcaps = (uint32_t)rbuf[9] | ((uint32_t)rbuf[10] << 8);
dmjs->caps.num_cs = rbuf[11];
dmjs->caps.min_bpw = rbuf[12];
dmjs->caps.max_bpw = rbuf[13];
dev_info(dev, " capabilities: freq=%d..%d, flgcaps=%04hx, bpw=%d..%d, num cs=%d\n",
dmjs->caps.freq_min, dmjs->caps.freq_max, dmjs->caps.flgcaps,
dmjs->caps.min_bpw, dmjs->caps.max_bpw, dmjs->caps.num_cs);
if (dmjs->caps.max_bpw == 0 || dmjs->caps.min_bpw == 0) {
dev_err(dev, "Device replied with max_bpw=0 or min_bpw=0, wtf?\n");
ret = -EXDEV;
}
if (!(dmjs->caps.flgcaps & (DMJ_SPI_S_CAP_STDSPI
| DMJ_SPI_S_CAP_TISSP | DMJ_SPI_S_CAP_MICROW))) {
dev_err(dev, "Device does not support any SPI mode, wtf?\n");
ret = -EXDEV;
}
kfree(rbuf);
}
return ret;
}
static int dmj_spi_set_freq(struct dmj_spi *dmjs, int ind, uint32_t freq)
{
struct device *dev = &dmjs->pdev->dev;
uint8_t wbuf[] = { DMJ_SPI_CMD_S_SPI_FREQ,
freq, freq >> 8, freq >> 16, freq >> 24 };
uint8_t *rbuf;
uint32_t freqret;
int ret, rlen;
if (dmjs->devsettings[ind].freq == freq) return 0;
dev_dbg(dev, "set freq to %u\n", freq);
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
ret = dmj_check_retval_sp(ret, rlen, dev, "set CS", true, 5, 5, rbuf);
if (!ret) {
freqret = (uint32_t)rbuf[1] | ((uint32_t)rbuf[2] << 8)
| ((uint32_t)rbuf[3] << 16) | ((uint32_t)rbuf[4] << 24);
if (freqret != freq) {
dev_warn(dev, "set frequency: couldn't provide exact freq %u Hz, %u Hz was applied instead.\n",
freq, freqret);
}
/* not the returned one, to avoid resending */
dmjs->devsettings[ind].freq = freq;
}
if (rbuf) kfree(rbuf);
return ret;
}
static int dmj_spi_set_flags(struct dmj_spi *dmjs, int ind, uint8_t flags)
{
struct device *dev = &dmjs->pdev->dev;
uint8_t wbuf[] = { DMJ_SPI_CMD_S_SPI_FLAGS, flags };
uint8_t *rbuf, flagret;
int ret, rlen;
if (dmjs->devsettings[ind].flags == flags) return 0;
dev_dbg(dev, "set flags %08x\n", flags);
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
ret = dmj_check_retval_sp(ret, rlen, dev, "set flags", true, 2, 2, rbuf);
if (!ret) {
flagret = rbuf[1];
if (flagret != flags) {
dev_warn(dev, "set flags: couldn't set exact flags %08x, was set to %08x instead\n",
flags, flagret);
}
/* not the returned one, to avoid resending */
dmjs->devsettings[ind].flags = flags;
}
if (rbuf) kfree(rbuf);
return ret;
}
static int dmj_spi_set_bpw(struct dmj_spi *dmjs, int ind, uint8_t bpw)
{
struct device *dev = &dmjs->pdev->dev;
uint8_t wbuf[] = { DMJ_SPI_CMD_S_SPI_BPW, bpw };
uint8_t *rbuf, bpwret;
int ret, rlen;
if (dmjs->devsettings[ind].bpw == bpw) return 0;
dev_dbg(dev, "set bpw %hhu\n", bpw);
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
ret = dmj_check_retval_sp(ret, rlen, dev, "set bpw", true, 2, 2, rbuf);
if (!ret) {
bpwret = rbuf[1];
if (bpwret != bpw) {
dev_warn(dev, "set flags: couldn't set exact bpw %hhu, was set to %hhu instead\n",
bpw, bpwret);
}
/* not the returned one, to avoid resending */
dmjs->devsettings[ind].bpw = bpw;
}
if (rbuf) kfree(rbuf);
return ret;
}
static int dmj_spi_set_pinstate(struct dmj_spi *dmjs, bool pins)
{
struct device *dev = &dmjs->pdev->dev;
uint8_t wbuf[] = { DMJ_SPI_CMD_S_PINSTATE, pins ? 1 : 0 };
uint8_t *rbuf;
int ret, rlen;
if (!has_cmd(dmjs, DMJ_SPI_CMD_S_PINSTATE)) return 0;
dev_dbg(dev, "set pinstate %sabled\n", pins?"en":"dis");
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
ret = dmj_check_retval_sp(ret, rlen, dev, "set pinstate", true, 2, 2, rbuf);
/*if (!ret) {
dmjs->devsettings[ind].pinst = pins;
}*/
if (rbuf) kfree(rbuf);
return ret;
}
static int dmj_spi_check_hw(struct dmj_spi *dmjs)
{
struct device *dev = &dmjs->pdev->dev;
uint8_t wbuf[] = { DMJ_SPI_CMD_NOP };
uint8_t *rbuf;
uint16_t iface;
int ret, rlen, i;
dev_dbg(dev, "check hw: nop");
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
ret = dmj_check_retval_sp(ret, rlen, dev, "check hw: nop", true, 1, 1, rbuf);
if (rbuf) kfree(rbuf);
if (ret) return ret;
dev_dbg(dev, "check hw: syncnop");
wbuf[0] = DMJ_SPI_CMD_SYNCNOP;
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
ret = dmj_check_retval(ret, rlen, dev, "check hw: nop", true, 2, 2);
if (!ret) {
if (rbuf[0] != DMJ_SPI_NAK || rbuf[1] != DMJ_SPI_ACK) {
dev_err(dev, "check hw: syncnop: bad response %02x %02x\n",
rbuf[0], rbuf[1]);
ret = -EIO;
}
}
if (rbuf) kfree(rbuf);
if (ret) return ret;
dev_dbg(dev, "check hw: iface");
wbuf[0] = DMJ_SPI_CMD_Q_IFACE;
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
ret = dmj_check_retval_sp(ret, rlen, dev, "check hw: iface", true, 3, 3, rbuf);
if (!ret) {
iface = (uint16_t)rbuf[1] | ((uint16_t)rbuf[2] << 8);
if (iface != SERPROG_IFACE_VERSION) {
dev_err(dev, "check hw: iface: bad serprog version: expected %hu, got %hu\n",
SERPROG_IFACE_VERSION, iface);
ret = -ENODEV;
}
}
if (rbuf) kfree(rbuf);
if (ret) return ret;
dev_dbg(dev, "check hw: cmdmap");
wbuf[0] = DMJ_SPI_CMD_Q_CMDMAP;
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
ret = dmj_check_retval_sp(ret, rlen, dev, "check hw: cmdmap", true, 33, 33, rbuf);
if (!ret) {
memcpy(dmjs->cmdmap, &rbuf[1], 32);
}
if (rbuf) kfree(rbuf);
if (ret) return ret;
for (i = 0; i < sizeof(reqd_cmds)/sizeof(*reqd_cmds); ++i) {
if (!has_cmd(dmjs, reqd_cmds[i])) {
dev_err(dev, "device does not have required serprog command %02x\n", reqd_cmds[i]);
ret = -ENODEV;
}
}
if (has_cmd(dmjs, DMJ_SPI_CMD_Q_PGMNAME)) {
dev_dbg(dev, "check hw: pgmname");
wbuf[0] = DMJ_SPI_CMD_Q_PGMNAME;
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
ret = dmj_check_retval_sp(ret, rlen, dev, "check hw: pgmname", true, 17, 17, rbuf);
if (!ret) {
rbuf[16] = 0;
dev_info(dev, "Serprog pgmname: '%s'\n", &rbuf[1]);
}
if (rbuf) kfree(rbuf);
if (ret) return ret;
}
if (has_cmd(dmjs, DMJ_SPI_CMD_Q_WRNMAXLEN)) {
dev_dbg(dev, "check hw: wrnmaxlen");
wbuf[0] = DMJ_SPI_CMD_Q_WRNMAXLEN;
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
ret = dmj_check_retval_sp(ret, rlen, dev, "check hw: wrnmaxlen", true, 4, 4, rbuf);
if (!ret) {
dmjs->wrnmaxlen = (uint32_t)rbuf[1] | ((uint32_t)rbuf[2] << 8) | ((uint32_t)rbuf[3] << 16);
}
if (rbuf) kfree(rbuf);
if (ret) return ret;
} else dmjs->rdnmaxlen = 512;
dev_info(dev, " wrnmaxlen = 0x%x\n", dmjs->wrnmaxlen);
if (has_cmd(dmjs, DMJ_SPI_CMD_Q_RDNMAXLEN)) {
dev_dbg(dev, "check hw: rdnmaxlen");
wbuf[0] = DMJ_SPI_CMD_Q_RDNMAXLEN;
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
ret = dmj_check_retval_sp(ret, rlen, dev, "check hw: rdnmaxlen", true, 4, 4, rbuf);
if (!ret) {
dmjs->rdnmaxlen = (uint32_t)rbuf[1] | ((uint32_t)rbuf[2] << 8) | ((uint32_t)rbuf[3] << 16);
}
if (rbuf) kfree(rbuf);
if (ret) return ret;
} else dmjs->rdnmaxlen = 512;
dev_info(dev, " rdnmaxlen = 0x%x\n", dmjs->rdnmaxlen);
return 0;
}
static int dmj_spi_do_read(struct dmj_spi *dmjs, void *data, size_t len, uint8_t bpw)
{
struct device *dev = &dmjs->pdev->dev;
uint8_t *rbuf;
int ret, rlen;
if (len > INT_MAX-4) return -EINVAL;
if (has_cmd(dmjs, DMJ_SPI_CMD_SPI_READ)) {
dev_dbg(dev, "do spi read len=0x%zx\n", len);
dmjs->txbuf[0] = DMJ_SPI_CMD_SPI_READ;
dmjs->txbuf[1] = len & 0xff;
dmjs->txbuf[2] = (len >> 8) & 0xff;
dmjs->txbuf[3] = (len >> 16) & 0xff;
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
dmjs->txbuf, 4, (void**)&rbuf, &rlen);
ret = dmj_check_retval_sp(ret, rlen, dev, "do read", true, (int)len+1, (int)len+1, rbuf);
if (!ret) bufconv_from_le(data, &rbuf[1], len, bpw);
if (rbuf) kfree(rbuf);
} else if (has_cmd(dmjs, DMJ_SPI_CMD_SPIOP)) {
dev_dbg(dev, "do spiop read len=0x%zx\n", len);
dmjs->txbuf[0] = DMJ_SPI_CMD_SPIOP;
dmjs->txbuf[1] = 0;
dmjs->txbuf[2] = 0;
dmjs->txbuf[3] = 0;
dmjs->txbuf[4] = len & 0xff;
dmjs->txbuf[5] = (len >> 8) & 0xff;
dmjs->txbuf[6] = (len >> 16) & 0xff;
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
dmjs->txbuf, 7, (void**)&rbuf, &rlen);
ret = dmj_check_retval_sp(ret, rlen, dev, "do spiop read", true, (int)len+1, (int)len+1, rbuf);
if (!ret) bufconv_from_le(data, &rbuf[1], len, bpw);
if (rbuf) kfree(rbuf);
} else if (has_cmd(dmjs, DMJ_SPI_CMD_SPI_RDWR)) {
dev_dbg(dev, "do rdwr read len=0x%zx\n", len);
dmjs->txbuf[0] = DMJ_SPI_CMD_SPI_RDWR;
dmjs->txbuf[1] = len & 0xff;
dmjs->txbuf[2] = (len >> 8) & 0xff;
dmjs->txbuf[3] = (len >> 16) & 0xff;
/* we need to fill the buffer with stuff bits to control the data
* that will get sent over the full duplex line. 0 is the default used
* in most places apparently? */
memset(&dmjs->txbuf[4], 0, len);
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
dmjs->txbuf, (int)len+4, (void**)&rbuf, &rlen);
ret = dmj_check_retval_sp(ret, rlen, dev, "do rdwr read", true, (int)len+1, (int)len+1, rbuf);
if (!ret) bufconv_from_le(data, &rbuf[1], len, bpw);
if (rbuf) kfree(rbuf);
} else {
return -EXDEV;
}
return 0;
}
static int dmj_spi_do_write(struct dmj_spi *dmjs, const void *data, size_t len, uint8_t bpw)
{
struct device *dev = &dmjs->pdev->dev;
uint8_t *rbuf;
int ret, rlen;
if (len > INT_MAX-7) return -EINVAL;
if (has_cmd(dmjs, DMJ_SPI_CMD_SPI_WRITE)) {
dev_dbg(dev, "do spi write len=0x%zx\n", len);
dmjs->txbuf[0] = DMJ_SPI_CMD_SPI_WRITE;
dmjs->txbuf[1] = len & 0xff;
dmjs->txbuf[2] = (len >> 8) & 0xff;
dmjs->txbuf[3] = (len >> 16) & 0xff;
bufconv_to_le(&dmjs->txbuf[4], data, len, bpw);
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
dmjs->txbuf, (int)len+4, (void**)&rbuf, &rlen);
ret = dmj_check_retval_sp(ret, rlen, dev, "do write", true, 1, 1, rbuf);
if (rbuf) kfree(rbuf);
} else if (has_cmd(dmjs, DMJ_SPI_CMD_SPIOP)) {
dev_dbg(dev, "do spiop write len=0x%zx\n", len);
dmjs->txbuf[0] = DMJ_SPI_CMD_SPIOP;
dmjs->txbuf[1] = len & 0xff;
dmjs->txbuf[2] = (len >> 8) & 0xff;
dmjs->txbuf[3] = (len >> 16) & 0xff;
dmjs->txbuf[4] = 0;
dmjs->txbuf[5] = 0;
dmjs->txbuf[6] = 0;
bufconv_to_le(&dmjs->txbuf[7], data, len, bpw);
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
dmjs->txbuf, (int)len+7, (void**)&rbuf, &rlen);
ret = dmj_check_retval_sp(ret, rlen, dev, "do spiop write", true, 1, 1, rbuf);
if (rbuf) kfree(rbuf);
} else if (has_cmd(dmjs, DMJ_SPI_CMD_SPI_RDWR)) {
dev_dbg(dev, "do rdwr write len=0x%zx\n", len);
dmjs->txbuf[0] = DMJ_SPI_CMD_SPI_RDWR;
dmjs->txbuf[1] = len & 0xff;
dmjs->txbuf[2] = (len >> 8) & 0xff;
dmjs->txbuf[3] = (len >> 16) & 0xff;
bufconv_to_le(&dmjs->txbuf[4], data, len, bpw);
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
dmjs->txbuf, (int)len+4, (void**)&rbuf, &rlen);
ret = dmj_check_retval_sp(ret, rlen, dev, "do rdwr write", true, (int)len+1, (int)len+1, rbuf);
/* we just don't look at the returned bytes in this case */
if (rbuf) kfree(rbuf);
} else {
return -EXDEV;
}
return 0;
}
/* should only be called if it already has the cmd anyway (cf. spi_controller->flags) */
static int dmj_spi_do_rdwr(struct dmj_spi *dmjs, void *rdata, const void *wdata, size_t len, uint8_t bpw)
{
struct device *dev = &dmjs->pdev->dev;
uint8_t *rbuf;
int ret, rlen;
if (len > INT_MAX-4) return -EINVAL;
if (!has_cmd(dmjs, DMJ_SPI_CMD_SPI_RDWR)) return -EXDEV;
dev_dbg(dev, "do rdwr len=0x%zx\n", len);
dmjs->txbuf[0] = DMJ_SPI_CMD_SPI_RDWR;
dmjs->txbuf[1] = len & 0xff;
dmjs->txbuf[2] = (len >> 8) & 0xff;
dmjs->txbuf[3] = (len >> 16) & 0xff;
bufconv_to_le(&dmjs->txbuf[4], wdata, len, bpw);
ret = dmj_transfer(dmjs->pdev, DMJ_CMD_MODE1_SPI, DMJ_XFER_FLAGS_PARSE_RESP,
dmjs->txbuf, (int)len+4, (void**)&rbuf, &rlen);
ret = dmj_check_retval_sp(ret, rlen, dev, "do rdwr", true, (int)len+1, -1, rbuf);
if (!ret) bufconv_from_le(rdata, &rbuf[1], len, bpw);
if (rbuf) kfree(rbuf);
return ret;
}
static int dmj_spi_prepare_message(struct spi_controller *spictl, struct spi_message *msg)
{
struct dmj_spi *dmjs = spi_controller_get_devdata(spictl);
struct spi_device *spidev = msg->spi;
struct device *dev = &spidev->dev;
int ret;
ret = dmj_spi_set_flags(dmjs, spidev->chip_select,
kernmode_to_flags(dmjs->caps.flgcaps, spidev->mode));
if (ret < 0) {
dev_err(dev, "Failed to set SPI flags\n");
return ret;
}
/*ret = dmj_spi_csmask_set_one(dmjs, spidev->chip_select);
if (ret < 0) {
dev_err(dev, "Failed to set CS mask\n");
return ret;
}*/
return ret;
}
static void dmj_spi_set_cs(struct spi_device *spidev, bool enable)
{
struct spi_controller *spictl = spidev->controller;
struct dmj_spi *dmjs = spi_controller_get_devdata(spictl);
struct device *dev = &spidev->dev;
int ret;
ret = dmj_spi_csmask_set_one(dmjs, spidev->chip_select);
if (ret < 0) {
dev_err(dev, "Failed to set CS mask\n");
return;
}
ret = dmj_spi_cs_set(dmjs, spidev->chip_select, enable);
if (ret < 0) {
dev_err(dev, "Failed to set chip select line\n");
return;
}
/*return 0;*/
}
static int dmj_spi_transfer_one(struct spi_controller *spictl, struct spi_device *spidev, struct spi_transfer *xfer)
{
struct dmj_spi *dmjs = spi_controller_get_devdata(spictl);
struct device *dev = &spidev->dev;
int ret;
uint32_t cksize, todo, off = 0;
ret = dmj_spi_set_freq(dmjs, spidev->chip_select, xfer->speed_hz);
if (ret < 0) {
dev_err(dev, "Failed to set SPI frequency to %d Hz\n", xfer->speed_hz);
return ret;
}
ret = dmj_spi_set_bpw(dmjs, spidev->chip_select, xfer->bits_per_word);
if (ret < 0) {
dev_err(dev, "Failed to set SPI bits-per-word to %d\n", xfer->bits_per_word);
return ret;
}
if (xfer->tx_buf && xfer->rx_buf) {
cksize = dmjs->wrnmaxlen;
if (cksize > dmjs->rdnmaxlen) cksize = dmjs->rdnmaxlen;
} else if (xfer->tx_buf) {
cksize = dmjs->wrnmaxlen;
} else if (xfer->rx_buf) {
cksize = dmjs->rdnmaxlen;
} else return -EINVAL;
todo = xfer->len;
do {
if (todo < cksize) cksize = todo;
if (xfer->tx_buf && xfer->rx_buf) {
ret = dmj_spi_do_rdwr(dmjs, xfer->rx_buf + off, xfer->tx_buf + off, cksize, xfer->bits_per_word);
} else if (xfer->tx_buf) {
ret = dmj_spi_do_write(dmjs, xfer->tx_buf + off, cksize, xfer->bits_per_word);
} else /*if (xfer->rx_buf)*/ {
ret = dmj_spi_do_read(dmjs, xfer->rx_buf + off, cksize, xfer->bits_per_word);
}
if (ret < 0) {
dev_err(dev, "SPI transfer failed! %d\n", ret);
return ret;
}
todo -= cksize;
off += cksize;
} while (todo);
return 0;
}
static int dmj_spi_probe(struct platform_device *pdev)
{
struct spi_controller *spictl;
struct dmj_spi *dmjs;
struct device *dev = &pdev->dev;
int ret, i;
spictl = spi_alloc_master(dev, sizeof(*dmjs));
if (!spictl ) return -ENOMEM;
platform_set_drvdata(pdev, spictl);
dmjs = spi_controller_get_devdata(spictl);
dmjs->spictl = spictl;
dmjs->spictl->dev.of_node = dev->of_node;
dmjs->pdev = pdev;
dmjs->csmask = 0xff;
for (i = 0; i < 8; ++i) {
dmjs->devsettings[i].freq = 0;
dmjs->devsettings[i].flags = 0xff;
dmjs->devsettings[i].bpw = 0xff;
}
spin_lock_init(&dmjs->csmap_lock);
ret = dmj_spi_check_hw(dmjs);
if (ret < 0) {
dev_err(dev, "Hardware capabilities lacking\n");
goto err_free_ctl;
}
dmjs->txbuf = devm_kmalloc(&pdev->dev, dmjs->wrnmaxlen + 0x10, GFP_KERNEL);
if (!dmjs->txbuf) {
ret = -ENOMEM;
dev_err(dev, "No memory left for TX buffer of length 0x%x\n", dmjs->wrnmaxlen);
goto err_free_ctl;
}
ret = dmj_spi_get_caps(dmjs);
if (ret < 0) {
dev_err(dev, "Failed to get device capabilities\n");
goto err_free_ctl;
}
spictl->min_speed_hz = dmjs->caps.freq_min;
spictl->max_speed_hz = dmjs->caps.freq_max;
spictl->bits_per_word_mask = SPI_BPW_RANGE_MASK(dmjs->caps.min_bpw, dmjs->caps.max_bpw);
spictl->num_chipselect = dmjs->caps.num_cs;
spictl->mode_bits = devcaps_to_kernmode(dmjs->caps.flgcaps);
spictl->bus_num = -1;
spictl->prepare_message = dmj_spi_prepare_message;
spictl->transfer_one = dmj_spi_transfer_one;
spictl->set_cs = dmj_spi_set_cs;
spictl->flags = 0;
if (!has_cmd(dmjs, DMJ_SPI_CMD_SPI_RDWR))
spictl->flags |= SPI_CONTROLLER_HALF_DUPLEX;
pm_runtime_set_autosuspend_delay(dev, DMJ_PINST_AUTOSUSPEND_TIMEOUT);
pm_runtime_use_autosuspend(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
ret = devm_spi_register_controller(dev, spictl);
if (ret < 0) {
dev_err(dev, "Failed to register SPI controller\n");
goto err_dereg;
}
dev_info(dev, "SPI bus number is %d\n", spictl->bus_num);
caps_to_binfo(dmjs, spictl->bus_num);
for (i = 0; i < dmjs->caps.num_cs; ++i) {
if (!spi_new_device(spictl, &dmjs->binfo[i])) {
dev_warn(dev, "failed to create %s device %d\n", dmjs->binfo[i].modalias, i);
}
}
return dmj_spi_set_pinstate(dmjs, true);
err_dereg:
pm_runtime_disable(dev);
pm_runtime_set_suspended(dev);
err_free_ctl:
spi_controller_put(spictl);
return ret;
}
static int dmj_spi_remove(struct platform_device *pdev)
{
struct spi_controller *spictl = platform_get_drvdata(pdev);
struct dmj_spi *dmjs = spi_controller_get_devdata(spictl);
pm_runtime_disable(&pdev->dev);
dmj_spi_set_pinstate(dmjs, false);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int dmj_spi_suspend(struct device *dev)
{
struct spi_controller *spictl = dev_get_drvdata(dev);
struct dmj_spi *dmjs = spi_controller_get_devdata(spictl);
int ret, i;
ret = spi_controller_suspend(spictl);
if (ret < 0) return ret;
dmj_spi_set_pinstate(dmjs, false);
for (i = 0; i < 8; ++i) {
dmjs->devsettings[i].freq = 0;
dmjs->devsettings[i].flags = 0xff;
dmjs->devsettings[i].bpw = 0xff;
}
return 0;
}
static int dmj_spi_resume(struct device *dev)
{
struct spi_controller *spictl = dev_get_drvdata(dev);
struct dmj_spi *dmjs = spi_controller_get_devdata(spictl);
dmj_spi_set_pinstate(dmjs, true);
return spi_controller_resume(spictl);
}
#endif /* CONFIG_PM_SLEEP */
#ifdef CONFIG_PM
static int dmj_spi_runtime_suspend(struct device *dev)
{
struct spi_controller *spictl = dev_get_drvdata(dev);
struct dmj_spi *dmjs = spi_controller_get_devdata(spictl);
return dmj_spi_set_pinstate(dmjs, false);
}
static int dmj_spi_runtime_resume(struct device *dev)
{
struct spi_controller *spictl = dev_get_drvdata(dev);
struct dmj_spi *dmjs = spi_controller_get_devdata(spictl);
return dmj_spi_set_pinstate(dmjs, true);
}
#endif /* CONFIG_PM */
static const struct dev_pm_ops dmj_spi_pm = {
SET_SYSTEM_SLEEP_PM_OPS(dmj_spi_suspend, dmj_spi_resume)
SET_RUNTIME_PM_OPS(dmj_spi_runtime_suspend, dmj_spi_runtime_resume, NULL)
};
static struct platform_driver spi_dmj_driver = {
.driver = {
.name = "dmj-spi",
.pm = &dmj_spi_pm,
},
.probe = dmj_spi_probe,
.remove = dmj_spi_remove
};
module_platform_driver(spi_dmj_driver);
MODULE_AUTHOR("sys64738 <sys64738@disroot.org>");
MODULE_AUTHOR("haskal <haskal@awoo.systems>");
MODULE_DESCRIPTION("SPI interface driver for the " HARDWARE_NAME " USB multitool");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:dmj-spi");

View File

@ -0,0 +1,984 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for the Dragon Probe USB multitool: USB-SPI adapter
*
* Copyright (c) 2021 sys64738 and haskal
*
* Adapted from:
* spi-dln2.c, Copyright (c) 2014 Intel Corporation
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/pm_runtime.h>
#include <asm/unaligned.h>
#if 0
#include <linux/mfd/dragonprobe.h>
#else
#include "dragonprobe.h"
#endif
#define HARDWARE_NAME "Dragon Probe"
#define DP_SPI_CMD_NOP 0x00
#define DP_SPI_CMD_Q_IFACE 0x01
#define DP_SPI_CMD_Q_CMDMAP 0x02
#define DP_SPI_CMD_Q_PGMNAME 0x03
#define DP_SPI_CMD_Q_SERBUF 0x04
#define DP_SPI_CMD_Q_BUSTYPE 0x05
#define DP_SPI_CMD_Q_CHIPSIZE 0x06
#define DP_SPI_CMD_Q_OPBUF 0x07
#define DP_SPI_CMD_Q_WRNMAXLEN 0x08
#define DP_SPI_CMD_R_BYTE 0x09
#define DP_SPI_CMD_R_NBYTES 0x0a
#define DP_SPI_CMD_O_INIT 0x0b
#define DP_SPI_CMD_O_WRITEB 0x0c
#define DP_SPI_CMD_O_WRITEN 0x0d
#define DP_SPI_CMD_O_DELAY 0x0e
#define DP_SPI_CMD_O_EXEC 0x0f
#define DP_SPI_CMD_SYNCNOP 0x10
#define DP_SPI_CMD_Q_RDNMAXLEN 0x11
#define DP_SPI_CMD_S_BUSTYPE 0x12
#define DP_SPI_CMD_SPIOP 0x13
#define DP_SPI_CMD_S_SPI_FREQ 0x14
#define DP_SPI_CMD_S_PINSTATE 0x15
#define DP_SPI_CMD_Q_SPI_CAPS 0x40
#define DP_SPI_CMD_S_SPI_CHIPN 0x41
#define DP_SPI_CMD_S_SPI_SETCS 0x42
#define DP_SPI_CMD_S_SPI_FLAGS 0x43
#define DP_SPI_CMD_S_SPI_BPW 0x44
#define DP_SPI_CMD_SPI_READ 0x45
#define DP_SPI_CMD_SPI_WRITE 0x46
#define DP_SPI_CMD_SPI_RDWR 0x47
#define SERPROG_IFACE_VERSION 0x0001
static const uint8_t reqd_cmds[] = {
DP_SPI_CMD_NOP, DP_SPI_CMD_Q_IFACE, DP_SPI_CMD_Q_CMDMAP,
/*DP_SPI_CMD_Q_WRNMAXLEN, DP_SPI_CMD_Q_RDNMAXLEN,*/
DP_SPI_CMD_S_SPI_FREQ, /*DP_SPI_CMD_S_PINSTATE,*/ /*DP_SPI_CMD_SPIOP,*/
DP_SPI_CMD_Q_SPI_CAPS, DP_SPI_CMD_S_SPI_CHIPN, DP_SPI_CMD_S_SPI_FLAGS,
DP_SPI_CMD_S_SPI_BPW, DP_SPI_CMD_S_SPI_SETCS,
/*DP_SPI_CMD_SPI_READ, DP_SPI_CMD_SPI_WRITE, DP_SPI_CMD_SPI_RDWR,*/
};
#define DP_SPI_ACK 0x06
#define DP_SPI_NAK 0x15
#define DP_SPI_S_FLG_CPHA (1<<0)
#define DP_SPI_S_FLG_CPOL (1<<1)
#define DP_SPI_S_FLG_STDSPI (0<<2)
#define DP_SPI_S_FLG_TISSP (1<<2)
#define DP_SPI_S_FLG_MICROW (2<<2)
#define DP_SPI_S_FLG_MSBFST (0<<4)
#define DP_SPI_S_FLG_LSBFST (1<<4)
#define DP_SPI_S_FLG_CSACLO (0<<5)
#define DP_SPI_S_FLG_CSACHI (1<<5)
#define DP_SPI_S_FLG_3WIRE (1<<6)
#define DP_SPI_S_CAP_CPHA_HI (1<<0)
#define DP_SPI_S_CAP_CPHA_LO (1<<1)
#define DP_SPI_S_CAP_CPOL_HI (1<<2)
#define DP_SPI_S_CAP_CPOL_LO (1<<3)
#define DP_SPI_S_CAP_STDSPI (1<<4)
#define DP_SPI_S_CAP_TISSP (1<<5)
#define DP_SPI_S_CAP_MICROW (1<<6)
#define DP_SPI_S_CAP_MSBFST (1<<7)
#define DP_SPI_S_CAP_LSBFST (1<<8)
#define DP_SPI_S_CAP_CSACHI (1<<9)
#define DP_SPI_S_CAP_3WIRE (1<<10)
#define DP_PINST_AUTOSUSPEND_TIMEOUT 2000
struct dp_spi_caps {
uint32_t freq_min, freq_max;
uint16_t flgcaps;
uint8_t num_cs, min_bpw, max_bpw;
};
struct dp_spi_dev_sett {
/* does not have to be guarded with a spinlock, as the kernel already
* serializes transfer_one/set_cs calls */
uint32_t freq;
uint8_t flags, bpw;
uint8_t cs, pinst;
};
struct dp_spi {
struct platform_device *pdev;
struct spi_controller *spictl;
uint8_t *txbuf;
struct dp_spi_caps caps;
uint8_t csmask;
struct dp_spi_dev_sett devsettings[8];
uint32_t wrnmaxlen, rdnmaxlen;
uint8_t cmdmap[32];
spinlock_t csmap_lock;
struct spi_board_info binfo[8];
};
static int dp_check_retval_sp(int ret, int len, struct device *dev,
const char *pfix, bool check_pos_val, int lmin, int lmax, const void* rbuf)
{
ret = dp_check_retval(ret, len, dev, pfix, check_pos_val, lmin, lmax);
if (ret >= 0 && ((const uint8_t *)rbuf)[0] != DP_SPI_ACK) {
dev_err(dev, "%s: did not receive ACK\n", pfix);
ret = -EIO;
}
return ret;
}
static bool has_cmd(struct dp_spi *dps, int cmd)
{
int byteind = cmd >> 3, bitind = cmd & 7;
return dps->cmdmap[byteind] & (1 << bitind);
}
static uint8_t kernmode_to_flags(uint16_t caps, int mode)
{
uint8_t ret = mode & 3; /* bottom 2 bits are the SPI mode (CPHA & CPOL) */
if (mode & SPI_LSB_FIRST) ret |= DP_SPI_S_FLG_LSBFST;
else ret |= DP_SPI_S_FLG_MSBFST;
if (mode & SPI_CS_HIGH) ret |= DP_SPI_S_FLG_CSACHI;
else ret |= DP_SPI_S_FLG_CSACLO;
if (mode & SPI_3WIRE) ret |= DP_SPI_S_FLG_3WIRE;
/* need some defaults for other stuff */
if (caps & DP_SPI_S_CAP_STDSPI) ret |= DP_SPI_S_FLG_STDSPI;
else if (caps & DP_SPI_S_CAP_TISSP) ret |= DP_SPI_S_FLG_TISSP;
else if (caps & DP_SPI_S_CAP_MICROW) ret |= DP_SPI_S_FLG_MICROW;
else ret |= DP_SPI_S_FLG_STDSPI; /* shrug, also shouldn't happen (cf. get_caps) */
return ret;
}
static int devcaps_to_kernmode(uint16_t caps)
{
int ret;
ret = caps & 3; /* SPI mode (CPHA & CPOL) bits */
if (caps & DP_SPI_S_CAP_LSBFST) ret |= SPI_LSB_FIRST;
if (caps & DP_SPI_S_CAP_CSACHI) ret |= SPI_CS_HIGH;
if (caps & DP_SPI_S_CAP_3WIRE) ret |= SPI_3WIRE;
return ret;
}
static void caps_to_binfo(struct dp_spi *dps, int busnum)
{
int i;
for (i = 0; i < dps->caps.num_cs; ++i) {
snprintf(dps->binfo[i].modalias, SPI_NAME_SIZE, "spidev");
dps->binfo[i].controller_data = dps;
dps->binfo[i].max_speed_hz = dps->caps.freq_max;
dps->binfo[i].bus_num = busnum;
dps->binfo[i].chip_select = i;
dps->binfo[i].mode = 0; /* shrug */
}
}
static void bufconv_to_le(void *dst, const void *src, size_t len_bytes, uint8_t bpw)
{
#ifdef __LITTLE_ENDIAN
memcpy(dst, src, len_bytes);
#else
if (bpw > 16) {
__le32 *dst32 = (__le32 *)dst;
const uint32_t *src32 = (const uint32_t *)src;
for (size_t i = 0; i < len_bytes; i += 4, ++dst32, ++src32) {
*dst32 = cpu_to_le32p(src32);
}
} else if (bpw > 8) {
__le16 *dst16 = (__le16 *)dst;
const uint16_t *src16 = (const uint16_t *)src;
for (size_t i = 0; i < len_bytes; i += 2, ++dst16, ++src16) {
*dst16 = cpu_to_le16p(src16);
}
} else {
memcpy(dst, src, len_bytes);
}
#endif
}
static void bufconv_from_le(void *dst, const void *src, size_t len_bytes, uint8_t bpw)
{
#ifdef __LITTLE_ENDIAN
memcpy(dst, src, len_bytes);
#else
if (bpw > 16) {
const __le32 *src32 = (const __le32 *)src;
uint32_t *dst32 = (uint32_t *)dst;
for (size_t i = 0; i < len_bytes; i += 4, ++dst32, ++src32) {
*dst32 = get_unaligned_le32(src32);
}
} else if (bpw > 8) {
const __le16 *src16 = (const __le16 *)src;
uint16_t *dst16 = (uint16_t *)dst;
for (size_t i = 0; i < len_bytes; i += 2, ++dst16, ++src16) {
*dst16 = le16_to_cpup(src16);
}
} else {
memcpy(dst, src, len_bytes);
}
#endif
}
static int dp_spi_csmask_set(struct dp_spi *dps, uint8_t csmask)
{
struct device *dev = &dps->pdev->dev;
uint8_t oldcm;
bool do_csmask = false;
uint8_t wbuf[] = { DP_SPI_CMD_S_SPI_CHIPN, csmask };
uint8_t *rbuf;
int ret, rlen;
spin_lock(&dps->csmap_lock);
oldcm = dps->csmask;
if (oldcm != csmask) {
dps->csmask = csmask;
do_csmask = true;
}
spin_unlock(&dps->csmap_lock);
if (do_csmask) {
dev_dbg(dev, "set csmask %02x\n", csmask);
ret = dp_transfer(dps->pdev, DP_CMD_MODE1_SPI, DP_XFER_FLAGS_PARSE_RESP,
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
ret = dp_check_retval_sp(ret, rlen, dev, "set CS mask", true, 1, 1, rbuf);
if (rbuf) kfree(rbuf);
}
return 0;
}
static int dp_spi_csmask_set_one(struct dp_spi *dps, uint8_t cs)
{
return dp_spi_csmask_set(dps, BIT(cs));
}
static int dp_spi_cs_set(struct dp_spi *dps, int ind, bool lvl)
{
struct device *dev = &dps->pdev->dev;
uint8_t wbuf[] = { DP_SPI_CMD_S_SPI_SETCS, lvl ? 1 : 0 };
uint8_t *rbuf;
int ret, rlen;
if (dps->devsettings[ind].cs == (lvl ? 1 : 0)) return 0;
dev_dbg(dev, "set cs %s\n", lvl?"hi":"lo");
ret = dp_transfer(dps->pdev, DP_CMD_MODE1_SPI, DP_XFER_FLAGS_PARSE_RESP,
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
ret = dp_check_retval_sp(ret, rlen, dev, "set CS", true, 1, 1, rbuf);
if (!ret) {
dps->devsettings[ind].cs = lvl ? 1 : 0;
}
if (rbuf) kfree(rbuf);
return ret;
}
static int dp_spi_get_caps(struct dp_spi *dps)
{
struct device *dev = &dps->pdev->dev;
uint8_t wbuf[] = { DP_SPI_CMD_Q_SPI_CAPS };
uint8_t *rbuf;
int ret, rlen;
dev_dbg(dev, "get caps\n");
ret = dp_transfer(dps->pdev, DP_CMD_MODE1_SPI, DP_XFER_FLAGS_PARSE_RESP,
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
ret = dp_check_retval_sp(ret, rlen, dev, "get caps", true, 14, 14, rbuf);
if (!ret) {
dps->caps.freq_min = (uint32_t)rbuf[1] | ((uint32_t)rbuf[2] << 8)
| ((uint32_t)rbuf[3] << 16) | ((uint32_t)rbuf[4] << 24);
dps->caps.freq_max = (uint32_t)rbuf[5] | ((uint32_t)rbuf[6] << 8)
| ((uint32_t)rbuf[7] << 16) | ((uint32_t)rbuf[8] << 24);
dps->caps.flgcaps = (uint32_t)rbuf[9] | ((uint32_t)rbuf[10] << 8);
dps->caps.num_cs = rbuf[11];
dps->caps.min_bpw = rbuf[12];
dps->caps.max_bpw = rbuf[13];
dev_info(dev, " capabilities: freq=%d..%d, flgcaps=%04hx, bpw=%d..%d, num cs=%d\n",
dps->caps.freq_min, dps->caps.freq_max, dps->caps.flgcaps,
dps->caps.min_bpw, dps->caps.max_bpw, dps->caps.num_cs);
if (dps->caps.max_bpw == 0 || dps->caps.min_bpw == 0) {
dev_err(dev, "Device replied with max_bpw=0 or min_bpw=0, wtf?\n");
ret = -EXDEV;
}
if (!(dps->caps.flgcaps & (DP_SPI_S_CAP_STDSPI
| DP_SPI_S_CAP_TISSP | DP_SPI_S_CAP_MICROW))) {
dev_err(dev, "Device does not support any SPI mode, wtf?\n");
ret = -EXDEV;
}
kfree(rbuf);
}
return ret;
}
static int dp_spi_set_freq(struct dp_spi *dps, int ind, uint32_t freq)
{
struct device *dev = &dps->pdev->dev;
uint8_t wbuf[] = { DP_SPI_CMD_S_SPI_FREQ,
freq, freq >> 8, freq >> 16, freq >> 24 };
uint8_t *rbuf;
uint32_t freqret;
int ret, rlen;
if (dps->devsettings[ind].freq == freq) return 0;
dev_dbg(dev, "set freq to %u\n", freq);
ret = dp_transfer(dps->pdev, DP_CMD_MODE1_SPI, DP_XFER_FLAGS_PARSE_RESP,
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
ret = dp_check_retval_sp(ret, rlen, dev, "set CS", true, 5, 5, rbuf);
if (!ret) {
freqret = (uint32_t)rbuf[1] | ((uint32_t)rbuf[2] << 8)
| ((uint32_t)rbuf[3] << 16) | ((uint32_t)rbuf[4] << 24);
if (freqret != freq) {
dev_warn(dev, "set frequency: couldn't provide exact freq %u Hz, %u Hz was applied instead.\n",
freq, freqret);
}
/* not the returned one, to avoid resending */
dps->devsettings[ind].freq = freq;
}
if (rbuf) kfree(rbuf);
return ret;
}
static int dp_spi_set_flags(struct dp_spi *dps, int ind, uint8_t flags)
{
struct device *dev = &dps->pdev->dev;
uint8_t wbuf[] = { DP_SPI_CMD_S_SPI_FLAGS, flags };
uint8_t *rbuf, flagret;
int ret, rlen;
if (dps->devsettings[ind].flags == flags) return 0;
dev_dbg(dev, "set flags %08x\n", flags);
ret = dp_transfer(dps->pdev, DP_CMD_MODE1_SPI, DP_XFER_FLAGS_PARSE_RESP,
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
ret = dp_check_retval_sp(ret, rlen, dev, "set flags", true, 2, 2, rbuf);
if (!ret) {
flagret = rbuf[1];
if (flagret != flags) {
dev_warn(dev, "set flags: couldn't set exact flags %08x, was set to %08x instead\n",
flags, flagret);
}
/* not the returned one, to avoid resending */
dps->devsettings[ind].flags = flags;
}
if (rbuf) kfree(rbuf);
return ret;
}
static int dp_spi_set_bpw(struct dp_spi *dps, int ind, uint8_t bpw)
{
struct device *dev = &dps->pdev->dev;
uint8_t wbuf[] = { DP_SPI_CMD_S_SPI_BPW, bpw };
uint8_t *rbuf, bpwret;
int ret, rlen;
if (dps->devsettings[ind].bpw == bpw) return 0;
dev_dbg(dev, "set bpw %hhu\n", bpw);
ret = dp_transfer(dps->pdev, DP_CMD_MODE1_SPI, DP_XFER_FLAGS_PARSE_RESP,
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
ret = dp_check_retval_sp(ret, rlen, dev, "set bpw", true, 2, 2, rbuf);
if (!ret) {
bpwret = rbuf[1];
if (bpwret != bpw) {
dev_warn(dev, "set flags: couldn't set exact bpw %hhu, was set to %hhu instead\n",
bpw, bpwret);
}
/* not the returned one, to avoid resending */
dps->devsettings[ind].bpw = bpw;
}
if (rbuf) kfree(rbuf);
return ret;
}
static int dp_spi_set_pinstate(struct dp_spi *dps, bool pins)
{
struct device *dev = &dps->pdev->dev;
uint8_t wbuf[] = { DP_SPI_CMD_S_PINSTATE, pins ? 1 : 0 };
uint8_t *rbuf;
int ret, rlen;
if (!has_cmd(dps, DP_SPI_CMD_S_PINSTATE)) return 0;
dev_dbg(dev, "set pinstate %sabled\n", pins?"en":"dis");
ret = dp_transfer(dps->pdev, DP_CMD_MODE1_SPI, DP_XFER_FLAGS_PARSE_RESP,
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
ret = dp_check_retval_sp(ret, rlen, dev, "set pinstate", true, 2, 2, rbuf);
/*if (!ret) {
dps->devsettings[ind].pinst = pins;
}*/
if (rbuf) kfree(rbuf);
return ret;
}
static int dp_spi_check_hw(struct dp_spi *dps)
{
struct device *dev = &dps->pdev->dev;
uint8_t wbuf[] = { DP_SPI_CMD_NOP };
uint8_t *rbuf;
uint16_t iface;
int ret, rlen, i;
dev_dbg(dev, "check hw: nop");
ret = dp_transfer(dps->pdev, DP_CMD_MODE1_SPI, DP_XFER_FLAGS_PARSE_RESP,
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
ret = dp_check_retval_sp(ret, rlen, dev, "check hw: nop", true, 1, 1, rbuf);
if (rbuf) kfree(rbuf);
if (ret) return ret;
dev_dbg(dev, "check hw: syncnop");
wbuf[0] = DP_SPI_CMD_SYNCNOP;
ret = dp_transfer(dps->pdev, DP_CMD_MODE1_SPI, DP_XFER_FLAGS_PARSE_RESP,
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
ret = dp_check_retval(ret, rlen, dev, "check hw: nop", true, 2, 2);
if (!ret) {
if (rbuf[0] != DP_SPI_NAK || rbuf[1] != DP_SPI_ACK) {
dev_err(dev, "check hw: syncnop: bad response %02x %02x\n",
rbuf[0], rbuf[1]);
ret = -EIO;
}
}
if (rbuf) kfree(rbuf);
if (ret) return ret;
dev_dbg(dev, "check hw: iface");
wbuf[0] = DP_SPI_CMD_Q_IFACE;
ret = dp_transfer(dps->pdev, DP_CMD_MODE1_SPI, DP_XFER_FLAGS_PARSE_RESP,
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
ret = dp_check_retval_sp(ret, rlen, dev, "check hw: iface", true, 3, 3, rbuf);
if (!ret) {
iface = (uint16_t)rbuf[1] | ((uint16_t)rbuf[2] << 8);
if (iface != SERPROG_IFACE_VERSION) {
dev_err(dev, "check hw: iface: bad serprog version: expected %hu, got %hu\n",
SERPROG_IFACE_VERSION, iface);
ret = -ENODEV;
}
}
if (rbuf) kfree(rbuf);
if (ret) return ret;
dev_dbg(dev, "check hw: cmdmap");
wbuf[0] = DP_SPI_CMD_Q_CMDMAP;
ret = dp_transfer(dps->pdev, DP_CMD_MODE1_SPI, DP_XFER_FLAGS_PARSE_RESP,
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
ret = dp_check_retval_sp(ret, rlen, dev, "check hw: cmdmap", true, 33, 33, rbuf);
if (!ret) {
memcpy(dps->cmdmap, &rbuf[1], 32);
}
if (rbuf) kfree(rbuf);
if (ret) return ret;
for (i = 0; i < sizeof(reqd_cmds)/sizeof(*reqd_cmds); ++i) {
if (!has_cmd(dps, reqd_cmds[i])) {
dev_err(dev, "device does not have required serprog command %02x\n", reqd_cmds[i]);
ret = -ENODEV;
}
}
if (has_cmd(dps, DP_SPI_CMD_Q_PGMNAME)) {
dev_dbg(dev, "check hw: pgmname");
wbuf[0] = DP_SPI_CMD_Q_PGMNAME;
ret = dp_transfer(dps->pdev, DP_CMD_MODE1_SPI, DP_XFER_FLAGS_PARSE_RESP,
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
ret = dp_check_retval_sp(ret, rlen, dev, "check hw: pgmname", true, 17, 17, rbuf);
if (!ret) {
rbuf[16] = 0;
dev_info(dev, "Serprog pgmname: '%s'\n", &rbuf[1]);
}
if (rbuf) kfree(rbuf);
if (ret) return ret;
}
if (has_cmd(dps, DP_SPI_CMD_Q_WRNMAXLEN)) {
dev_dbg(dev, "check hw: wrnmaxlen");
wbuf[0] = DP_SPI_CMD_Q_WRNMAXLEN;
ret = dp_transfer(dps->pdev, DP_CMD_MODE1_SPI, DP_XFER_FLAGS_PARSE_RESP,
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
ret = dp_check_retval_sp(ret, rlen, dev, "check hw: wrnmaxlen", true, 4, 4, rbuf);
if (!ret) {
dps->wrnmaxlen = (uint32_t)rbuf[1] | ((uint32_t)rbuf[2] << 8) | ((uint32_t)rbuf[3] << 16);
}
if (rbuf) kfree(rbuf);
if (ret) return ret;
} else dps->rdnmaxlen = 512;
dev_info(dev, " wrnmaxlen = 0x%x\n", dps->wrnmaxlen);
if (has_cmd(dps, DP_SPI_CMD_Q_RDNMAXLEN)) {
dev_dbg(dev, "check hw: rdnmaxlen");
wbuf[0] = DP_SPI_CMD_Q_RDNMAXLEN;
ret = dp_transfer(dps->pdev, DP_CMD_MODE1_SPI, DP_XFER_FLAGS_PARSE_RESP,
wbuf, sizeof(wbuf), (void**)&rbuf, &rlen);
ret = dp_check_retval_sp(ret, rlen, dev, "check hw: rdnmaxlen", true, 4, 4, rbuf);
if (!ret) {
dps->rdnmaxlen = (uint32_t)rbuf[1] | ((uint32_t)rbuf[2] << 8) | ((uint32_t)rbuf[3] << 16);
}
if (rbuf) kfree(rbuf);
if (ret) return ret;
} else dps->rdnmaxlen = 512;
dev_info(dev, " rdnmaxlen = 0x%x\n", dps->rdnmaxlen);
return 0;
}
static int dp_spi_do_read(struct dp_spi *dps, void *data, size_t len, uint8_t bpw)
{
struct device *dev = &dps->pdev->dev;
uint8_t *rbuf;
int ret, rlen;
if (len > INT_MAX-4) return -EINVAL;
if (has_cmd(dps, DP_SPI_CMD_SPI_READ)) {
dev_dbg(dev, "do spi read len=0x%zx\n", len);
dps->txbuf[0] = DP_SPI_CMD_SPI_READ;
dps->txbuf[1] = len & 0xff;
dps->txbuf[2] = (len >> 8) & 0xff;
dps->txbuf[3] = (len >> 16) & 0xff;
ret = dp_transfer(dps->pdev, DP_CMD_MODE1_SPI, DP_XFER_FLAGS_PARSE_RESP,
dps->txbuf, 4, (void**)&rbuf, &rlen);
ret = dp_check_retval_sp(ret, rlen, dev, "do read", true, (int)len+1, (int)len+1, rbuf);
if (!ret) bufconv_from_le(data, &rbuf[1], len, bpw);
if (rbuf) kfree(rbuf);
} else if (has_cmd(dps, DP_SPI_CMD_SPIOP)) {
dev_dbg(dev, "do spiop read len=0x%zx\n", len);
dps->txbuf[0] = DP_SPI_CMD_SPIOP;
dps->txbuf[1] = 0;
dps->txbuf[2] = 0;
dps->txbuf[3] = 0;
dps->txbuf[4] = len & 0xff;
dps->txbuf[5] = (len >> 8) & 0xff;
dps->txbuf[6] = (len >> 16) & 0xff;
ret = dp_transfer(dps->pdev, DP_CMD_MODE1_SPI, DP_XFER_FLAGS_PARSE_RESP,
dps->txbuf, 7, (void**)&rbuf, &rlen);
ret = dp_check_retval_sp(ret, rlen, dev, "do spiop read", true, (int)len+1, (int)len+1, rbuf);
if (!ret) bufconv_from_le(data, &rbuf[1], len, bpw);
if (rbuf) kfree(rbuf);
} else if (has_cmd(dps, DP_SPI_CMD_SPI_RDWR)) {
dev_dbg(dev, "do rdwr read len=0x%zx\n", len);
dps->txbuf[0] = DP_SPI_CMD_SPI_RDWR;
dps->txbuf[1] = len & 0xff;
dps->txbuf[2] = (len >> 8) & 0xff;
dps->txbuf[3] = (len >> 16) & 0xff;
/* we need to fill the buffer with stuff bits to control the data
* that will get sent over the full duplex line. 0 is the default used
* in most places apparently? */
memset(&dps->txbuf[4], 0, len);
ret = dp_transfer(dps->pdev, DP_CMD_MODE1_SPI, DP_XFER_FLAGS_PARSE_RESP,
dps->txbuf, (int)len+4, (void**)&rbuf, &rlen);
ret = dp_check_retval_sp(ret, rlen, dev, "do rdwr read", true, (int)len+1, (int)len+1, rbuf);
if (!ret) bufconv_from_le(data, &rbuf[1], len, bpw);
if (rbuf) kfree(rbuf);
} else {
return -EXDEV;
}
return 0;
}
static int dp_spi_do_write(struct dp_spi *dps, const void *data, size_t len, uint8_t bpw)
{
struct device *dev = &dps->pdev->dev;
uint8_t *rbuf;
int ret, rlen;
if (len > INT_MAX-7) return -EINVAL;
if (has_cmd(dps, DP_SPI_CMD_SPI_WRITE)) {
dev_dbg(dev, "do spi write len=0x%zx\n", len);
dps->txbuf[0] = DP_SPI_CMD_SPI_WRITE;
dps->txbuf[1] = len & 0xff;
dps->txbuf[2] = (len >> 8) & 0xff;
dps->txbuf[3] = (len >> 16) & 0xff;
bufconv_to_le(&dps->txbuf[4], data, len, bpw);
ret = dp_transfer(dps->pdev, DP_CMD_MODE1_SPI, DP_XFER_FLAGS_PARSE_RESP,
dps->txbuf, (int)len+4, (void**)&rbuf, &rlen);
ret = dp_check_retval_sp(ret, rlen, dev, "do write", true, 1, 1, rbuf);
if (rbuf) kfree(rbuf);
} else if (has_cmd(dps, DP_SPI_CMD_SPIOP)) {
dev_dbg(dev, "do spiop write len=0x%zx\n", len);
dps->txbuf[0] = DP_SPI_CMD_SPIOP;
dps->txbuf[1] = len & 0xff;
dps->txbuf[2] = (len >> 8) & 0xff;
dps->txbuf[3] = (len >> 16) & 0xff;
dps->txbuf[4] = 0;
dps->txbuf[5] = 0;
dps->txbuf[6] = 0;
bufconv_to_le(&dps->txbuf[7], data, len, bpw);
ret = dp_transfer(dps->pdev, DP_CMD_MODE1_SPI, DP_XFER_FLAGS_PARSE_RESP,
dps->txbuf, (int)len+7, (void**)&rbuf, &rlen);
ret = dp_check_retval_sp(ret, rlen, dev, "do spiop write", true, 1, 1, rbuf);
if (rbuf) kfree(rbuf);
} else if (has_cmd(dps, DP_SPI_CMD_SPI_RDWR)) {
dev_dbg(dev, "do rdwr write len=0x%zx\n", len);
dps->txbuf[0] = DP_SPI_CMD_SPI_RDWR;
dps->txbuf[1] = len & 0xff;
dps->txbuf[2] = (len >> 8) & 0xff;
dps->txbuf[3] = (len >> 16) & 0xff;
bufconv_to_le(&dps->txbuf[4], data, len, bpw);
ret = dp_transfer(dps->pdev, DP_CMD_MODE1_SPI, DP_XFER_FLAGS_PARSE_RESP,
dps->txbuf, (int)len+4, (void**)&rbuf, &rlen);
ret = dp_check_retval_sp(ret, rlen, dev, "do rdwr write", true, (int)len+1, (int)len+1, rbuf);
/* we just don't look at the returned bytes in this case */
if (rbuf) kfree(rbuf);
} else {
return -EXDEV;
}
return 0;
}
/* should only be called if it already has the cmd anyway (cf. spi_controller->flags) */
static int dp_spi_do_rdwr(struct dp_spi *dps, void *rdata, const void *wdata, size_t len, uint8_t bpw)
{
struct device *dev = &dps->pdev->dev;
uint8_t *rbuf;
int ret, rlen;
if (len > INT_MAX-4) return -EINVAL;
if (!has_cmd(dps, DP_SPI_CMD_SPI_RDWR)) return -EXDEV;
dev_dbg(dev, "do rdwr len=0x%zx\n", len);
dps->txbuf[0] = DP_SPI_CMD_SPI_RDWR;
dps->txbuf[1] = len & 0xff;
dps->txbuf[2] = (len >> 8) & 0xff;
dps->txbuf[3] = (len >> 16) & 0xff;
bufconv_to_le(&dps->txbuf[4], wdata, len, bpw);
ret = dp_transfer(dps->pdev, DP_CMD_MODE1_SPI, DP_XFER_FLAGS_PARSE_RESP,
dps->txbuf, (int)len+4, (void**)&rbuf, &rlen);
ret = dp_check_retval_sp(ret, rlen, dev, "do rdwr", true, (int)len+1, -1, rbuf);
if (!ret) bufconv_from_le(rdata, &rbuf[1], len, bpw);
if (rbuf) kfree(rbuf);
return ret;
}
static int dp_spi_prepare_message(struct spi_controller *spictl, struct spi_message *msg)
{
struct dp_spi *dps = spi_controller_get_devdata(spictl);
struct spi_device *spidev = msg->spi;
struct device *dev = &spidev->dev;
int ret;
ret = dp_spi_set_flags(dps, spidev->chip_select,
kernmode_to_flags(dps->caps.flgcaps, spidev->mode));
if (ret < 0) {
dev_err(dev, "Failed to set SPI flags\n");
return ret;
}
/*ret = dp_spi_csmask_set_one(dps, spidev->chip_select);
if (ret < 0) {
dev_err(dev, "Failed to set CS mask\n");
return ret;
}*/
return ret;
}
static void dp_spi_set_cs(struct spi_device *spidev, bool enable)
{
struct spi_controller *spictl = spidev->controller;
struct dp_spi *dps = spi_controller_get_devdata(spictl);
struct device *dev = &spidev->dev;
int ret;
ret = dp_spi_csmask_set_one(dps, spidev->chip_select);
if (ret < 0) {
dev_err(dev, "Failed to set CS mask\n");
return;
}
ret = dp_spi_cs_set(dps, spidev->chip_select, enable);
if (ret < 0) {
dev_err(dev, "Failed to set chip select line\n");
return;
}
/*return 0;*/
}
static int dp_spi_transfer_one(struct spi_controller *spictl, struct spi_device *spidev, struct spi_transfer *xfer)
{
struct dp_spi *dps = spi_controller_get_devdata(spictl);
struct device *dev = &spidev->dev;
int ret;
uint32_t cksize, todo, off = 0;
ret = dp_spi_set_freq(dps, spidev->chip_select, xfer->speed_hz);
if (ret < 0) {
dev_err(dev, "Failed to set SPI frequency to %d Hz\n", xfer->speed_hz);
return ret;
}
ret = dp_spi_set_bpw(dps, spidev->chip_select, xfer->bits_per_word);
if (ret < 0) {
dev_err(dev, "Failed to set SPI bits-per-word to %d\n", xfer->bits_per_word);
return ret;
}
if (xfer->tx_buf && xfer->rx_buf) {
cksize = dps->wrnmaxlen;
if (cksize > dps->rdnmaxlen) cksize = dps->rdnmaxlen;
} else if (xfer->tx_buf) {
cksize = dps->wrnmaxlen;
} else if (xfer->rx_buf) {
cksize = dps->rdnmaxlen;
} else return -EINVAL;
todo = xfer->len;
do {
if (todo < cksize) cksize = todo;
if (xfer->tx_buf && xfer->rx_buf) {
ret = dp_spi_do_rdwr(dps, xfer->rx_buf + off, xfer->tx_buf + off, cksize, xfer->bits_per_word);
} else if (xfer->tx_buf) {
ret = dp_spi_do_write(dps, xfer->tx_buf + off, cksize, xfer->bits_per_word);
} else /*if (xfer->rx_buf)*/ {
ret = dp_spi_do_read(dps, xfer->rx_buf + off, cksize, xfer->bits_per_word);
}
if (ret < 0) {
dev_err(dev, "SPI transfer failed! %d\n", ret);
return ret;
}
todo -= cksize;
off += cksize;
} while (todo);
return 0;
}
static int dp_spi_probe(struct platform_device *pdev)
{
struct spi_controller *spictl;
struct dp_spi *dps;
struct device *dev = &pdev->dev;
int ret, i;
spictl = spi_alloc_master(dev, sizeof(*dps));
if (!spictl ) return -ENOMEM;
platform_set_drvdata(pdev, spictl);
dps = spi_controller_get_devdata(spictl);
dps->spictl = spictl;
dps->spictl->dev.of_node = dev->of_node;
dps->pdev = pdev;
dps->csmask = 0xff;
for (i = 0; i < 8; ++i) {
dps->devsettings[i].freq = 0;
dps->devsettings[i].flags = 0xff;
dps->devsettings[i].bpw = 0xff;
}
spin_lock_init(&dps->csmap_lock);
ret = dp_spi_check_hw(dps);
if (ret < 0) {
dev_err(dev, "Hardware capabilities lacking\n");
goto err_free_ctl;
}
dps->txbuf = devm_kmalloc(&pdev->dev, dps->wrnmaxlen + 0x10, GFP_KERNEL);
if (!dps->txbuf) {
ret = -ENOMEM;
dev_err(dev, "No memory left for TX buffer of length 0x%x\n", dps->wrnmaxlen);
goto err_free_ctl;
}
ret = dp_spi_get_caps(dps);
if (ret < 0) {
dev_err(dev, "Failed to get device capabilities\n");
goto err_free_ctl;
}
spictl->min_speed_hz = dps->caps.freq_min;
spictl->max_speed_hz = dps->caps.freq_max;
spictl->bits_per_word_mask = SPI_BPW_RANGE_MASK(dps->caps.min_bpw, dps->caps.max_bpw);
spictl->num_chipselect = dps->caps.num_cs;
spictl->mode_bits = devcaps_to_kernmode(dps->caps.flgcaps);
spictl->bus_num = -1;
spictl->prepare_message = dp_spi_prepare_message;
spictl->transfer_one = dp_spi_transfer_one;
spictl->set_cs = dp_spi_set_cs;
spictl->flags = 0;
if (!has_cmd(dps, DP_SPI_CMD_SPI_RDWR))
spictl->flags |= SPI_CONTROLLER_HALF_DUPLEX;
pm_runtime_set_autosuspend_delay(dev, DP_PINST_AUTOSUSPEND_TIMEOUT);
pm_runtime_use_autosuspend(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
ret = devm_spi_register_controller(dev, spictl);
if (ret < 0) {
dev_err(dev, "Failed to register SPI controller\n");
goto err_dereg;
}
dev_info(dev, "SPI bus number is %d\n", spictl->bus_num);
caps_to_binfo(dps, spictl->bus_num);
for (i = 0; i < dps->caps.num_cs; ++i) {
if (!spi_new_device(spictl, &dps->binfo[i])) {
dev_warn(dev, "failed to create %s device %d\n", dps->binfo[i].modalias, i);
}
}
return dp_spi_set_pinstate(dps, true);
err_dereg:
pm_runtime_disable(dev);
pm_runtime_set_suspended(dev);
err_free_ctl:
spi_controller_put(spictl);
return ret;
}
static int dp_spi_remove(struct platform_device *pdev)
{
struct spi_controller *spictl = platform_get_drvdata(pdev);
struct dp_spi *dps = spi_controller_get_devdata(spictl);
pm_runtime_disable(&pdev->dev);
dp_spi_set_pinstate(dps, false);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int dp_spi_suspend(struct device *dev)
{
struct spi_controller *spictl = dev_get_drvdata(dev);
struct dp_spi *dps = spi_controller_get_devdata(spictl);
int ret, i;
ret = spi_controller_suspend(spictl);
if (ret < 0) return ret;
dp_spi_set_pinstate(dps, false);
for (i = 0; i < 8; ++i) {
dps->devsettings[i].freq = 0;
dps->devsettings[i].flags = 0xff;
dps->devsettings[i].bpw = 0xff;
}
return 0;
}
static int dp_spi_resume(struct device *dev)
{
struct spi_controller *spictl = dev_get_drvdata(dev);
struct dp_spi *dps = spi_controller_get_devdata(spictl);
dp_spi_set_pinstate(dps, true);
return spi_controller_resume(spictl);
}
#endif /* CONFIG_PM_SLEEP */
#ifdef CONFIG_PM
static int dp_spi_runtime_suspend(struct device *dev)
{
struct spi_controller *spictl = dev_get_drvdata(dev);
struct dp_spi *dps = spi_controller_get_devdata(spictl);
return dp_spi_set_pinstate(dps, false);
}
static int dp_spi_runtime_resume(struct device *dev)
{
struct spi_controller *spictl = dev_get_drvdata(dev);
struct dp_spi *dps = spi_controller_get_devdata(spictl);
return dp_spi_set_pinstate(dps, true);
}
#endif /* CONFIG_PM */
static const struct dev_pm_ops dp_spi_pm = {
SET_SYSTEM_SLEEP_PM_OPS(dp_spi_suspend, dp_spi_resume)
SET_RUNTIME_PM_OPS(dp_spi_runtime_suspend, dp_spi_runtime_resume, NULL)
};
static struct platform_driver spi_dp_driver = {
.driver = {
.name = "dragonprobe-spi",
.pm = &dp_spi_pm,
},
.probe = dp_spi_probe,
.remove = dp_spi_remove
};
module_platform_driver(spi_dp_driver);
MODULE_AUTHOR("sys64738 <sys64738@disroot.org>");
MODULE_AUTHOR("haskal <haskal@awoo.systems>");
MODULE_DESCRIPTION("SPI interface driver for the " HARDWARE_NAME " USB multitool");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:dragonprobe-spi");

View File

@ -4,9 +4,9 @@ with open("../README.md", "r") as f:
long_description = f.read()
setup(
name='dmctl',
name='dpctl',
version='0.1.0',
description='Tool for controlling a dmj device',
description='Tool for controlling a Dragon Probe device',
long_description=long_description,
long_description_content_type='text/markdown',
url='',
@ -31,7 +31,7 @@ setup(
include_package_data=True,
entry_points={
'console_scripts': [
"dmctl=dmctl:main"
"dpctl=dpctl:main"
]
},
zip_safe=False

View File

@ -5,7 +5,7 @@
#include "bsp-info.h"
#define INFO_MANUFACTURER "BLAHAJ CTF"
#define INFO_PRODUCT_BARE "CMSIS-DAP"
#define INFO_PRODUCT_BARE "DragonProbe"
#define INFO_PRODUCT(board) INFO_PRODUCT_BARE " (" board ")"
#endif

View File

@ -304,11 +304,14 @@ static const uint8_t desc_configuration[] = {
#endif
};
static const char* string_desc_arr[] = {
/*[STRID_LANGID] = (const char[]){0x09, 0x04}, // supported language is English (0x0409)
[STRID_MANUFACTURER] = "BLAHAJ CTF", // Manufacturer
[STRID_PRODUCT] = "Dragnbus (RP2040 Pico)", // Product*/
NULL,
// no hw info here, or the name will be too long >__>
// CMSIS-DAP spec:
// "The Product String must contain 'CMSIS-DAP' somewhere in the string.
// This is used by the debuggers to identify a CMSIS-DAP compliant Debug
// Unit that is connected to a host computer."
[STRID_PRODUCT] = INFO_PRODUCT_BARE " CMSIS-DAP",
[STRID_CONFIG] = "Configuration descriptor",
// max string length check: |||||||||||||||||||||||||||||||
[STRID_IF_VND_CFG ] = "Device cfg/ctl interface",

View File

@ -185,15 +185,17 @@ const uint16_t* mode_std_descriptor_string_cb(uint8_t index, uint16_t langid) {
chr_count = 1;
} else if (index == STRID_SERIAL) {
chr_count = get_unique_id_u16(_desc_str + 1);
} else if (index > STRID_CONFIG && mode_current != NULL && mode_current->string_desc != NULL) {
if (index >= mode_current->n_string_desc) return NULL;
} else if (mode_current != NULL && mode_current->string_desc != NULL) {
if (index >= mode_current->n_string_desc) goto fallback;
const char* str = mode_current->string_desc[index];
if (!str) goto fallback;
chr_count = TU_MIN(strlen(str), 31);
for (int i = 0; i < chr_count; i++) { _desc_str[1 + i] = str[i]; }
} else {
fallback:
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors