laforge has uploaded this change for review. (
https://gerrit.osmocom.org/c/dahdi-linux/+/27093 )
Change subject: icE1usb: GPSDO status support in sysfs
......................................................................
icE1usb: GPSDO status support in sysfs
* ability to read GPSDO related bits via sysfs and ioctl
* ability to set GPSDO mode via sysfs
* show firmware build during driver start and expose it to sysfs
Change-Id: I7bc7daa4738b1db37fdd67945d7e976e2581b417
---
M drivers/dahdi/icE1usb/icE1usb.c
1 file changed, 380 insertions(+), 11 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/dahdi-linux refs/changes/93/27093/1
diff --git a/drivers/dahdi/icE1usb/icE1usb.c b/drivers/dahdi/icE1usb/icE1usb.c
index 87a7422..4ce29c6 100644
--- a/drivers/dahdi/icE1usb/icE1usb.c
+++ b/drivers/dahdi/icE1usb/icE1usb.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/* Osmocom icE1usb driver
- * Copyright (C) 2020 by Harald Welte <laforge(a)osmocom.org>
+ * Copyright (C) 2020-2022 by Harald Welte <laforge(a)osmocom.org>
* inspired by osmo-e1d by Sylvain Munaut */
/* The Osmocom icE1usb is a modern, software-defined open hardware and
@@ -52,15 +52,39 @@
#define ieu_err(x, fmt, args ...) \
dev_err(&((x)->usb_intf->dev), fmt, ## args)
+static const struct usb_device_id ice1usb_products[] = {
+ {
+ USB_DEVICE(0x1d50, 0x6145),
+ },
+ {}
+};
+
/***********************************************************************
* data structures
***********************************************************************/
+/* per-device global state */
+struct ice1usb_gpsdo {
+ /* USB device we operate on */
+ struct usb_device *usb_dev;
+ struct usb_interface *usb_intf;
+
+ struct e1usb_gpsdo_status gpsdo_status;
+ char fw_build[128];
+
+ /* is the device still present (true) or already absent/unplugged (false) */
+ bool present;
+ /* spinlock protecting concurrent access to fc, {read,write}chunk_idx, ... */
+ spinlock_t lock;
+};
+
+
enum ice1usb_flags {
ICE1USB_ISOC_RUNNING,
ICE1USB_IRQ_RUNNING,
};
+/* per-interface state */
struct ice1usb {
/* USB device and interface we operate on */
struct usb_device *usb_dev;
@@ -197,11 +221,13 @@
}
#define USB_RT_VEND_IF (USB_TYPE_VENDOR | USB_RECIP_INTERFACE)
+#define USB_RT_VEND_DEV (USB_TYPE_VENDOR | USB_RECIP_DEVICE)
/* synchronous request, may block up to 1s, only called from process context! */
static int ice1usb_tx_config(struct ice1usb *ieu)
{
int rc;
+ uint8_t if_num = ieu->usb_intf->cur_altsetting->desc.bInterfaceNumber;
ieu_info(ieu, "TX-CONFIG (crc4=%s, timing=%s, ext_loopback=%s,
tx_yellow_alarm=%u)\n",
tx_mode_str(ieu->cfg.tx.mode), tx_timing_str(ieu->cfg.tx.timing),
@@ -209,7 +235,7 @@
rc = usb_control_msg(ieu->usb_dev, usb_sndctrlpipe(ieu->usb_dev, 0),
ICE1USB_INTF_SET_TX_CFG, USB_RT_VEND_IF,
- 0, 0, &ieu->cfg.tx, sizeof(ieu->cfg.tx),
+ 0, if_num, &ieu->cfg.tx, sizeof(ieu->cfg.tx),
USB_CTRL_SET_TIMEOUT);
if (rc < 0)
return rc;
@@ -849,7 +875,327 @@
};
/***********************************************************************
- * kernel USB integration / probing
+* USB GPSDO driver (sysfs files) - NOT _interface_ driver
+***********************************************************************/
+
+static void ice1usb_gpsdo_free(struct ice1usb_gpsdo *e1d)
+{
+ usb_put_dev(e1d->usb_dev);
+ kfree(e1d);
+}
+
+/* The GPS-DO exists only once for the entire USB device, while all the E1 related
+ * bits exist once per USB interface! */
+
+/* update our internal, cached GPS-DO state */
+static int e1u_update_gpsdo(struct ice1usb_gpsdo *e1d)
+{
+ uint8_t if_num = e1d->usb_intf->cur_altsetting->desc.bInterfaceNumber;
+ int rc;
+
+ rc = usb_control_msg(e1d->usb_dev, usb_rcvctrlpipe(e1d->usb_dev, 0),
+ ICE1USB_INTF_GET_GPSDO_STATUS, USB_DIR_IN | USB_RT_VEND_IF,
+ 0, if_num, &e1d->gpsdo_status, sizeof(e1d->gpsdo_status),
+ USB_CTRL_SET_TIMEOUT);
+ if (rc < 0) {
+ dev_err(&e1d->usb_intf->dev, "Error during GPSDO CTRL GET transfer:
%d\n", rc);
+ return rc;
+ }
+ if (rc != sizeof(e1d->gpsdo_status)) {
+ dev_err(&e1d->usb_dev->dev, "Short read during GPSDO CTRL GET transfer:
%d\n", rc);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static const char *gpsdo_mode_str(enum ice1usb_gpsdo_mode mode)
+{
+ switch (mode) {
+ case ICE1USB_GPSDO_MODE_DISABLED:
+ return "disabled";
+ case ICE1USB_GPSDO_MODE_AUTO:
+ return "auto";
+ default:
+ return "invalid";
+ }
+}
+
+static const char *gpsdo_antenna_state_str(enum ice1usb_gpsdo_antenna_state st)
+{
+ switch (st) {
+ case ICE1USB_GPSDO_ANT_UNKNOWN:
+ return "unknown";
+ case ICE1USB_GPSDO_ANT_OK:
+ return "ok";
+ case ICE1USB_GPSDO_ANT_OPEN:
+ return "open";
+ case ICE1USB_GPSDO_ANT_SHORT:
+ return "short";
+ default:
+ return "invalid";
+ }
+}
+
+static const char *gpsdo_state_str(enum ice1usb_gpsdo_state st)
+{
+ switch (st) {
+ case ICE1USB_GPSDO_STATE_DISABLED:
+ return "disabled";
+ case ICE1USB_GPSDO_STATE_CALIBRATE:
+ return "calibrate";
+ case ICE1USB_GPSDO_STATE_HOLD_OVER:
+ return "hold_over";
+ case ICE1USB_GPSDO_STATE_TUNE_COARSE:
+ return "tune_coarse";
+ case ICE1USB_GPSDO_STATE_TUNE_FINE:
+ return "tune_fine";
+ default:
+ return "invalid";
+ }
+}
+
+
+static ssize_t e1u_gpsdo_mode_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct ice1usb_gpsdo *e1d = dev_get_drvdata(dev);
+ int rc = e1u_update_gpsdo(e1d);
+ if (rc < 0)
+ return rc;
+ return sprintf(buf, "%s\n", gpsdo_mode_str(e1d->gpsdo_status.mode));
+}
+
+static ssize_t e1u_gpsdo_mode_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ice1usb_gpsdo *e1d = dev_get_drvdata(dev);
+ uint8_t if_num = e1d->usb_intf->cur_altsetting->desc.bInterfaceNumber;
+ uint16_t imode;
+ int rc;
+
+ if (!strcmp(buf, "disabled"))
+ imode = ICE1USB_GPSDO_MODE_DISABLED;
+ else if (!strcmp(buf, "auto"))
+ imode = ICE1USB_GPSDO_MODE_AUTO;
+ else
+ return -EINVAL;
+
+ rc = usb_control_msg(e1d->usb_dev, usb_sndctrlpipe(e1d->usb_dev, 0),
+ ICE1USB_INTF_SET_GPSDO_MODE, USB_RT_VEND_IF,
+ imode, if_num, NULL, 0, USB_CTRL_SET_TIMEOUT);
+ if (rc < 0) {
+ dev_err(&e1d->usb_intf->dev, "Error during GPSDO CTRL SET transfer:
%d\n", rc);
+ return -EIO;
+ }
+ e1d->gpsdo_status.mode = imode;
+
+ return count;
+}
+
+static DEVICE_ATTR(gpsdo_mode, 0644, e1u_gpsdo_mode_show, e1u_gpsdo_mode_store);
+
+static ssize_t e1u_gpsdo_ant_state_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ice1usb_gpsdo *e1d = dev_get_drvdata(dev);
+ int rc = e1u_update_gpsdo(e1d);
+ if (rc < 0)
+ return rc;
+ return sprintf(buf, "%s\n",
gpsdo_antenna_state_str(e1d->gpsdo_status.antenna_state));
+}
+
+static DEVICE_ATTR(gpsdo_antenna_state, 0444, e1u_gpsdo_ant_state_show, NULL);
+
+static ssize_t e1u_gpsdo_state_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ice1usb_gpsdo *e1d = dev_get_drvdata(dev);
+ int rc = e1u_update_gpsdo(e1d);
+ if (rc < 0)
+ return rc;
+ return sprintf(buf, "%s\n", gpsdo_state_str(e1d->gpsdo_status.state));
+}
+
+static DEVICE_ATTR(gpsdo_state, 0444, e1u_gpsdo_state_show, NULL);
+
+static ssize_t e1u_gpsdo_valid_fix_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ice1usb_gpsdo *e1d = dev_get_drvdata(dev);
+ int rc = e1u_update_gpsdo(e1d);
+ if (rc < 0)
+ return rc;
+ return sprintf(buf, "%u\n", e1d->gpsdo_status.valid_fix);
+}
+
+static DEVICE_ATTR(gpsdo_valid_fix, 0444, e1u_gpsdo_valid_fix_show, NULL);
+
+
+static ssize_t e1u_gpsdo_tune_coarse_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ice1usb_gpsdo *e1d = dev_get_drvdata(dev);
+ int rc = e1u_update_gpsdo(e1d);
+ if (rc < 0)
+ return rc;
+ return sprintf(buf, "%u\n", e1d->gpsdo_status.tune.coarse);
+}
+
+/* TODO: set tuning */
+static DEVICE_ATTR(gpsdo_tune_coarse, 0444, e1u_gpsdo_tune_coarse_show, NULL);
+
+
+static ssize_t e1u_gpsdo_tune_fine_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ice1usb_gpsdo *e1d = dev_get_drvdata(dev);
+ int rc = e1u_update_gpsdo(e1d);
+ if (rc < 0)
+ return rc;
+ return sprintf(buf, "%u\n", e1d->gpsdo_status.tune.fine);
+}
+
+/* TODO: set tuning */
+static DEVICE_ATTR(gpsdo_tune_fine, 0444, e1u_gpsdo_tune_fine_show, NULL);
+
+static ssize_t e1u_gpsdo_freq_est_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ice1usb_gpsdo *e1d = dev_get_drvdata(dev);
+ int rc = e1u_update_gpsdo(e1d);
+ if (rc < 0)
+ return rc;
+ return sprintf(buf, "%u\n", e1d->gpsdo_status.freq_est);
+}
+
+static DEVICE_ATTR(gpsdo_freq_est, 0444, e1u_gpsdo_freq_est_show, NULL);
+
+static ssize_t e1u_fw_build_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ice1usb_gpsdo *e1d = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", e1d->fw_build);
+}
+
+static DEVICE_ATTR(fw_build, 0444, e1u_fw_build_show, NULL);
+
+static void create_sysfs_files(struct ice1usb_gpsdo *e1d)
+{
+ device_create_file(&e1d->usb_intf->dev, &dev_attr_gpsdo_mode);
+ device_create_file(&e1d->usb_intf->dev, &dev_attr_gpsdo_antenna_state);
+ device_create_file(&e1d->usb_intf->dev, &dev_attr_gpsdo_state);
+ device_create_file(&e1d->usb_intf->dev, &dev_attr_gpsdo_valid_fix);
+ device_create_file(&e1d->usb_intf->dev, &dev_attr_gpsdo_tune_coarse);
+ device_create_file(&e1d->usb_intf->dev, &dev_attr_gpsdo_tune_fine);
+ device_create_file(&e1d->usb_intf->dev, &dev_attr_gpsdo_freq_est);
+ device_create_file(&e1d->usb_intf->dev, &dev_attr_fw_build);
+}
+
+static void remove_sysfs_files(struct ice1usb_gpsdo *e1d)
+{
+ device_remove_file(&e1d->usb_intf->dev, &dev_attr_gpsdo_mode);
+ device_remove_file(&e1d->usb_intf->dev, &dev_attr_gpsdo_antenna_state);
+ device_remove_file(&e1d->usb_intf->dev, &dev_attr_gpsdo_state);
+ device_remove_file(&e1d->usb_intf->dev, &dev_attr_gpsdo_valid_fix);
+ device_remove_file(&e1d->usb_intf->dev, &dev_attr_gpsdo_tune_coarse);
+ device_remove_file(&e1d->usb_intf->dev, &dev_attr_gpsdo_tune_fine);
+ device_remove_file(&e1d->usb_intf->dev, &dev_attr_gpsdo_freq_est);
+ device_remove_file(&e1d->usb_intf->dev, &dev_attr_fw_build);
+}
+
+static int ice1usb_gpsdo_probe(struct usb_interface *intf, const struct usb_device_id
*prod)
+{
+ struct usb_device *usb_dev = usb_get_dev(interface_to_usbdev(intf));
+ const struct usb_interface_descriptor *ifdesc = &intf->altsetting->desc;
+ struct ice1usb_gpsdo *e1d;
+ int ret = -ENODEV;
+ int rc;
+
+ dev_dbg(&intf->dev, "entering %s", __FUNCTION__);
+
+ if (ifdesc->bInterfaceClass != 0xff || ifdesc->bInterfaceSubClass != 0xE1) {
+ dev_dbg(&intf->dev, "Unsupported Interface Class/SubClass %02x/%02x",
+ ifdesc->bInterfaceClass, ifdesc->bInterfaceSubClass);
+ ret = -ENODEV;
+ goto error;
+ }
+
+ /* we only support protocol 0 so far */
+ if (ifdesc->bInterfaceProtocol != 0xd0) {
+ ret = -ENODEV;
+ goto error;
+ }
+
+ e1d = kzalloc(sizeof(*e1d), GFP_KERNEL);
+ if (!e1d) {
+ dev_err(&intf->dev, "Out of memory\n");
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ e1d->usb_dev = usb_dev;
+ e1d->usb_intf = intf;
+ e1d->present = true;
+ spin_lock_init(&e1d->lock);
+
+ usb_set_intfdata(intf, e1d);
+
+ /* obtain firmware build information */
+ e1d->fw_build[0] = '\0';
+ rc = usb_control_msg(e1d->usb_dev, usb_rcvctrlpipe(e1d->usb_dev, 0),
+ ICE1USB_DEV_GET_FW_BUILD, USB_DIR_IN | USB_RT_VEND_DEV,
+ 0, 0, e1d->fw_build, sizeof(e1d->fw_build), USB_CTRL_SET_TIMEOUT);
+ if (rc < 0) {
+ dev_err(&e1d->usb_intf->dev, "Error during FW BUILD CTRL GET transfer:
%d\n", rc);
+ ret = rc;
+ goto error_free;
+ }
+ e1d->fw_build[sizeof(e1d->fw_build)-1] = '\0';
+ dev_info(&e1d->usb_intf->dev, "icE1usb firmware build:
'%s'\n", e1d->fw_build);
+
+ create_sysfs_files(e1d);
+
+ return 0;
+
+error_free:
+ usb_set_intfdata(intf, NULL);
+ kfree(e1d);
+error:
+ usb_put_dev(usb_dev);
+ return ret;
+}
+
+static void ice1usb_gpsdo_disconnect(struct usb_interface *intf)
+{
+ struct ice1usb_gpsdo *e1d = usb_get_intfdata(intf);
+
+ dev_dbg(&intf->dev, "entering %s", __FUNCTION__);
+
+ if (!e1d)
+ return;
+
+ /* avoid any shutdown code from submitting further I/O */
+ e1d->present = false;
+
+ remove_sysfs_files(e1d);
+
+ usb_set_intfdata(intf, NULL);
+
+ ice1usb_gpsdo_free(e1d);
+}
+
+
+static struct usb_driver ice1usb_gpsdo_driver = {
+ .name = "icE1usb-GPSDO",
+ .id_table = ice1usb_products,
+ .probe = ice1usb_gpsdo_probe,
+ .disconnect = ice1usb_gpsdo_disconnect,
+};
+
+/***********************************************************************
+ * kernel USB integration / probing
***********************************************************************/
/* does the given altsetting contain an EP with wMaxPacketSize == 0 ? */
@@ -989,6 +1335,10 @@
return -ENODEV;
}
+ /* we only support protocol 0 so far */
+ if (ifdesc->bInterfaceProtocol != 0x00)
+ return -ENODEV;
+
ieu = kzalloc(sizeof(*ieu), GFP_KERNEL);
if (!ieu) {
dev_err(&intf->dev, "Out of memory\n");
@@ -1119,13 +1469,6 @@
}
#endif
-static const struct usb_device_id ice1usb_products[] = {
- {
- USB_DEVICE(0x1d50, 0x6145),
- },
- {}
-};
-
static struct usb_driver ice1usb_driver = {
.name = "icE1usb",
.id_table = ice1usb_products,
@@ -1135,7 +1478,33 @@
//.resume = ice1usb_resume,
};
-module_usb_driver(ice1usb_driver);
+
+/* cannot use module_usb_driver() as we have both E1 and GPS-DO drivers */
+static int __init __ice1usb_init(void)
+{
+ int rc;
+
+ rc = usb_register_driver(&ice1usb_gpsdo_driver, THIS_MODULE, KBUILD_MODNAME);
+ if (rc < 0)
+ return rc;
+
+ rc = usb_register_driver(&ice1usb_driver, THIS_MODULE, KBUILD_MODNAME);
+ if (rc < 0) {
+ usb_deregister(&ice1usb_gpsdo_driver);
+ return rc;
+ }
+
+ return rc;
+}
+
+static void __exit __ice1usb_exit(void)
+{
+ usb_deregister(&ice1usb_gpsdo_driver);
+ usb_deregister(&ice1usb_driver);
+}
+
+module_init(__ice1usb_init);
+module_exit(__ice1usb_exit);
MODULE_AUTHOR("Harald Welte <laforge(a)osmocom.org>")>");
MODULE_DESCRIPTION("Osmocom icE1usb USB E1 interface");
--
To view, visit
https://gerrit.osmocom.org/c/dahdi-linux/+/27093
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings
Gerrit-Project: dahdi-linux
Gerrit-Branch: master
Gerrit-Change-Id: I7bc7daa4738b1db37fdd67945d7e976e2581b417
Gerrit-Change-Number: 27093
Gerrit-PatchSet: 1
Gerrit-Owner: laforge <laforge(a)osmocom.org>
Gerrit-MessageType: newchange