rebrand (we have finally found a non-temporary name)
This commit is contained in:
parent
c21a9ec431
commit
6c970432fe
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
|
@ -1,3 +0,0 @@
|
|||
#!/bin/sh
|
||||
cd "$(dirname "$(realpath "$0")")"
|
||||
exec python3 -m dmctl "$@"
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
cd "$(dirname "$(realpath "$0")")"
|
||||
exec python3 -m "$(basename -s .sh "$0")" "$@"
|
|
@ -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)
|
||||
|
|
@ -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()
|
|
@ -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
|
|
@ -179,7 +179,7 @@ class UsbConn(DevConn):
|
|||
|
||||
|
||||
class ChardevConn(DevConn):
|
||||
_DEVCLASSNAME = "dmj"
|
||||
_DEVCLASSNAME = "dragonprobe"
|
||||
|
||||
def try_find() -> Optional[ChardevConn]:
|
||||
if sys.platform != 'linux':
|
|
@ -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')
|
|
@ -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,
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
modules.order
|
||||
Module.symvers
|
||||
*.ko
|
||||
*.mod
|
||||
*.o
|
||||
*.mod.c
|
||||
.*.cmd
|
||||
.*.d
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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");
|
|
@ -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");
|
||||
|
|
@ -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");
|
|
@ -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
|
|
@ -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");
|
||||
|
|
@ -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");
|
|
@ -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");
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue