Message ID | 20230312190435.3568212-1-xiang.ye@intel.com |
---|---|
Headers | show |
Series | Add Intel LJCA device driver | expand |
On Mon, Mar 13, 2023 at 03:04:31AM +0800, Ye Xiang wrote: > This patch implements the USB part of Intel USB-I2C/GPIO/SPI adapter > device named "La Jolla Cove Adapter" (LJCA). > > The communication between the various LJCA module drivers and the > hardware will be muxed/demuxed by this driver. The sub-module of > LJCA can use ljca_transfer() to issue a transfer between host > and hardware. So you have 2 different things happening in this driver. One is the USB interaction and control and stuff, and one is the creation of an api that is to be used by other parts of the kernel. Can you split this up into 2 different commits, one for the api, and one for the USB stuff? I think you will find that the API is going to need a bunch of work, as it's not "normal" for what the kernel is expecting to have. Some other review comments below: > +struct ljca_event_cb_entry { > + ljca_event_cb_t notify; > + void *context; Why a void *? > +}; > + > +struct ljca_dev { > + struct usb_device *udev; > + struct usb_interface *intf; If you have the interface, you can get the usb device. Why store both? > + u8 in_ep; /* the address of the bulk in endpoint */ > + u8 out_ep; /* the address of the bulk out endpoint */ > + > + /* the urb/buffer for read */ > + struct urb *in_urb; > + unsigned char *ibuf; > + size_t ibuf_len; > + > + bool started; You can't just use a boolean as a "flag" without any locking, that is not going to work, sorry. > + struct list_head stubs_list; > + > + /* to wait for an ongoing write ack */ > + wait_queue_head_t ack_wq; > + > + struct mfd_cell *cells; > + int cell_count; > + /* mutex to protect package transfer with LJCA device */ > + struct mutex mutex; Why is this not protecting "started" as well as the other fields in this structure? > +}; > + > +struct ljca { > + u8 type; > + struct ljca_dev *dev; > +}; > + > +struct ljca_stub_packet { > + unsigned int *ibuf_len; > + u8 *ibuf; > +}; > + > +struct ljca_stub { > + struct list_head list; > + struct usb_interface *intf; > + struct ljca_stub_packet ipacket; > + u8 type; > + > + /* for identify ack */ > + bool acked; > + int cur_cmd; > + > + struct ljca_event_cb_entry event_entry; > + /* lock to protect event_entry */ > + spinlock_t event_cb_lock; > + > + struct ljca ljca; > + unsigned long priv[]; What is "priv" for? Why is it unsigned long and not a real type? > +}; > + > +static inline void *ljca_priv(struct ljca_stub *stub) > +{ > + return stub->priv; Why is this a void *? > +} > + > +static bool ljca_validate(struct ljca_msg *header, u32 data_len) > +{ > + return header->len + sizeof(*header) == data_len; > +} > + > +static struct ljca_stub *ljca_stub_alloc(struct ljca_dev *dev, u8 type, int priv_size) > +{ > + struct ljca_stub *stub; > + > + stub = kzalloc(struct_size(stub, priv, priv_size), GFP_KERNEL); > + if (!stub) > + return ERR_PTR(-ENOMEM); > + > + stub->type = type; > + stub->intf = dev->intf; > + stub->ljca.dev = dev; You are saving a reference to a reference counted device, yet never grabbing the reference? That is ripe for disaster. > + stub->ljca.type = stub->type; > + spin_lock_init(&stub->event_cb_lock); > + list_add_tail(&stub->list, &dev->stubs_list); Where is the reference counting on this new structure that you just created? Who controls the lifespan of it? > + return stub; > +} > + > +static struct ljca_stub *ljca_stub_find(struct ljca_dev *dev, u8 type) > +{ > + struct ljca_stub *stub; > + > + list_for_each_entry(stub, &dev->stubs_list, list) { > + if (stub->type == type) > + return stub; > + } > + > + dev_err(&dev->intf->dev, "USB stub not found, type:%d\n", type); > + > + return ERR_PTR(-ENODEV); > +} > + > +static void ljca_stub_notify(struct ljca_stub *stub, u8 cmd, const void *evt_data, int len) > +{ > + unsigned long flags; > + > + spin_lock_irqsave(&stub->event_cb_lock, flags); > + if (stub->event_entry.notify && stub->event_entry.context) > + stub->event_entry.notify(stub->event_entry.context, cmd, evt_data, len); > + spin_unlock_irqrestore(&stub->event_cb_lock, flags); > +} > + > +static int ljca_parse(struct ljca_dev *dev, struct ljca_msg *header) > +{ > + struct ljca_stub *stub; > + > + stub = ljca_stub_find(dev, header->type); > + if (IS_ERR(stub)) > + return PTR_ERR(stub); > + > + if (!(header->flags & LJCA_ACK_FLAG)) { > + ljca_stub_notify(stub, header->cmd, header->data, header->len); > + return 0; > + } > + > + if (stub->cur_cmd != header->cmd) { > + dev_err(&dev->intf->dev, "header and stub current command mismatch (%x vs %x)\n", > + header->cmd, stub->cur_cmd); > + return -EINVAL; > + } > + > + if (stub->ipacket.ibuf && stub->ipacket.ibuf_len) { > + unsigned int newlen; > + > + newlen = min_t(unsigned int, header->len, *stub->ipacket.ibuf_len); > + > + *stub->ipacket.ibuf_len = newlen; > + memcpy(stub->ipacket.ibuf, header->data, newlen); > + } > + > + stub->acked = true; > + wake_up(&dev->ack_wq); > + > + return 0; > +} > + > +static int ljca_stub_write(struct ljca_stub *stub, u8 cmd, const void *obuf, unsigned int obuf_len, Why is obuf a void *? It's a real structure (or u8 stream), make it so. > + void *ibuf, unsigned int *ibuf_len, bool wait_ack, unsigned long timeout) Same for ibuf. > +{ > + struct ljca_dev *dev = usb_get_intfdata(stub->intf); > + u8 flags = LJCA_CMPL_FLAG; > + struct ljca_msg *header; > + unsigned int msg_len = sizeof(*header) + obuf_len; > + int actual; > + int ret; > + > + if (msg_len > LJCA_MAX_PACKET_SIZE) > + return -EINVAL; > + > + if (wait_ack) > + flags |= LJCA_ACK_FLAG; > + > + header = kmalloc(msg_len, GFP_KERNEL); > + if (!header) > + return -ENOMEM; > + > + header->type = stub->type; > + header->cmd = cmd; > + header->flags = flags; > + header->len = obuf_len; > + > + if (obuf) > + memcpy(header->data, obuf, obuf_len); > + > + dev_dbg(&dev->intf->dev, "send: type:%d cmd:%d flags:%d len:%d\n", header->type, > + header->cmd, header->flags, header->len); > + > + usb_autopm_get_interface(dev->intf); > + if (!dev->started) { > + kfree(header); > + ret = -ENODEV; > + goto error_put; > + } > + > + mutex_lock(&dev->mutex); > + stub->cur_cmd = cmd; > + stub->ipacket.ibuf = ibuf; > + stub->ipacket.ibuf_len = ibuf_len; > + stub->acked = false; > + ret = usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, dev->out_ep), header, msg_len, > + &actual, LJCA_USB_WRITE_TIMEOUT_MS); > + kfree(header); > + if (ret) { > + dev_err(&dev->intf->dev, "bridge write failed ret:%d\n", ret); > + goto error_unlock; > + } > + > + if (actual != msg_len) { > + dev_err(&dev->intf->dev, "bridge write length mismatch (%d vs %d)\n", msg_len, > + actual); > + ret = -EINVAL; > + goto error_unlock; > + } > + > + if (wait_ack) { > + ret = wait_event_timeout(dev->ack_wq, stub->acked, msecs_to_jiffies(timeout)); > + if (!ret) { > + dev_err(&dev->intf->dev, "acked wait timeout\n"); > + ret = -ETIMEDOUT; > + goto error_unlock; > + } > + } > + > + stub->ipacket.ibuf = NULL; > + stub->ipacket.ibuf_len = NULL; > + ret = 0; > +error_unlock: > + mutex_unlock(&dev->mutex); > +error_put: > + usb_autopm_put_interface(dev->intf); > + > + return ret; > +} > + > +static int ljca_transfer_internal(struct ljca *ljca, u8 cmd, const void *obuf, > + unsigned int obuf_len, void *ibuf, unsigned int *ibuf_len, > + bool wait_ack) > +{ > + struct ljca_stub *stub; > + > + stub = ljca_stub_find(ljca->dev, ljca->type); > + if (IS_ERR(stub)) > + return PTR_ERR(stub); > + > + return ljca_stub_write(stub, cmd, obuf, obuf_len, ibuf, ibuf_len, wait_ack, > + LJCA_USB_WRITE_ACK_TIMEOUT_MS); > +} > + > +int ljca_transfer(struct ljca *ljca, u8 cmd, const void *obuf, unsigned int obuf_len, void *ibuf, > + unsigned int *ibuf_len) > +{ > + return ljca_transfer_internal(ljca, cmd, obuf, obuf_len, ibuf, ibuf_len, true); > +} > +EXPORT_SYMBOL_NS_GPL(ljca_transfer, LJCA); > + > +int ljca_transfer_noack(struct ljca *ljca, u8 cmd, const void *obuf, unsigned int obuf_len) > +{ > + return ljca_transfer_internal(ljca, cmd, obuf, obuf_len, NULL, NULL, false); > +} > +EXPORT_SYMBOL_NS_GPL(ljca_transfer_noack, LJCA); > + > +int ljca_register_event_cb(struct ljca *ljca, ljca_event_cb_t event_cb, void *context) What are these magic events you are registering? What do they do and why would anyone need them? You have global functions here that other drivers are using, yet no documentation about them at all for any way to review that the api really is doing what you are wanting it to do. So again, please split this up into at least 2 changes, and document this new api you are creating, so that we have a chance to review it properly. Otherwise it's almost impossible to do so. thanks, greg k-h
Hi Ye, On top of what Greg has already said, few things from my side through the lines. [...] > +static int ljca_mng_link(struct ljca_dev *dev, struct ljca_stub *stub) > +{ > + int ret; > + > + ret = ljca_mng_reset_handshake(stub); > + if (ret) > + return ret; > + > + /* try to enum all the stubs */ > + ljca_mng_enum_gpio(stub); > + ljca_mng_enum_i2c(stub); > + ljca_mng_enum_spi(stub); We are ignoring here the return value. So either make the whole function call chain to be void or please check the return values here. [...] > +static ssize_t ljca_enable_dfu_store(struct device *dev, struct device_attribute *attr, > + const char *buf, size_t count) > +{ > + struct usb_interface *intf = to_usb_interface(dev); > + struct ljca_dev *ljca_dev = usb_get_intfdata(intf); > + struct ljca_stub *mng_stub = ljca_stub_find(ljca_dev, LJCA_MNG_STUB); > + bool enable; > + int ret; > + > + ret = kstrtobool(buf, &enable); > + if (ret) > + return ret; > + > + if (enable) { > + ret = ljca_mng_set_dfu_mode(mng_stub); > + if (ret) > + return ret; > + } What is the DFU mode? Is it an operational mode? Do we enter and exit from it? Does the device leave this mode on its own? What if I write twice in a raw enable? Can I check if I am in DFU mode or not? Would you mind adding some comments here? > + > + return count; > +} > +static DEVICE_ATTR_WO(ljca_enable_dfu); > + > +static ssize_t ljca_trace_level_store(struct device *dev, struct device_attribute *attr, > + const char *buf, size_t count) > +{ > + struct usb_interface *intf = to_usb_interface(dev); > + struct ljca_dev *ljca_dev = usb_get_intfdata(intf); > + struct ljca_stub *diag_stub = ljca_stub_find(ljca_dev, LJCA_DIAG_STUB); > + u8 level; > + int ret; > + > + if (kstrtou8(buf, 0, &level)) > + return -EINVAL; > + > + ret = ljca_diag_set_trace_level(diag_stub, level); > + if (ret) > + return ret; do we need any range check for the levels? What happens if I do: echo "I am too cool" > /sys/.../ljca_trace_level As there were questions here, would you mind adding some comments so that the next reader won't make the same questions? > + > + return count; > +} > +static DEVICE_ATTR_WO(ljca_trace_level); [...] > +static int ljca_probe(struct usb_interface *intf, const struct usb_device_id *id) > +{ > + struct ljca_dev *dev; > + struct usb_endpoint_descriptor *bulk_in, *bulk_out; > + int ret; > + > + /* allocate memory for our device state and initialize it */ > + dev = kzalloc(sizeof(*dev), GFP_KERNEL); devm_kzalloc() Thanks, Andi > + if (!dev) > + return -ENOMEM;
On Mon, 13 Mar 2023, Ye Xiang wrote: > This patch implements the USB part of Intel USB-I2C/GPIO/SPI adapter > device named "La Jolla Cove Adapter" (LJCA). > > The communication between the various LJCA module drivers and the > hardware will be muxed/demuxed by this driver. The sub-module of > LJCA can use ljca_transfer() to issue a transfer between host > and hardware. > > Each sub-module of LJCA device is identified by type field within > the LJCA message header. > > The minimum code in ASL that covers this board is > Scope (\_SB.PCI0.DWC3.RHUB.HS01) > { > Device (GPIO) > { > Name (_ADR, Zero) > Name (_STA, 0x0F) > } > > Device (I2C) > { > Name (_ADR, One) > Name (_STA, 0x0F) > } > > Device (SPI) > { > Name (_ADR, 0x02) > Name (_STA, 0x0F) > } > } > > Signed-off-by: Ye Xiang <xiang.ye@intel.com> > Reviewed-by: Sakari Ailus <sakari.ailus@linux.intel.com> > --- > drivers/usb/misc/Kconfig | 13 + > drivers/usb/misc/Makefile | 1 + > drivers/usb/misc/ljca.c | 998 ++++++++++++++++++++++++++++++++++++++ > include/linux/usb/ljca.h | 95 ++++ > 4 files changed, 1107 insertions(+) > create mode 100644 drivers/usb/misc/ljca.c > create mode 100644 include/linux/usb/ljca.h > > diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig > index a5f7652db7da..59ec120c26d4 100644 > --- a/drivers/usb/misc/Kconfig > +++ b/drivers/usb/misc/Kconfig > @@ -273,6 +273,19 @@ config USB_LINK_LAYER_TEST > Layer Test Device. Say Y only when you want to conduct USB Super Speed > Link Layer Test for host controllers. > > +config USB_LJCA > + tristate "Intel La Jolla Cove Adapter support" > + select MFD_CORE > + depends on USB > + help > + This adds support for Intel La Jolla Cove USB-I2C/SPI/GPIO > + Master Adapter (LJCA). Additional drivers such as I2C_LJCA, > + GPIO_LJCA and SPI_LJCA must be enabled in order to use the > + functionality of the device. > + > + This driver can also be built as a module. If so, the module > + will be called ljca. > + > config USB_CHAOSKEY > tristate "ChaosKey random number generator driver support" > depends on HW_RANDOM > diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile > index 93581baec3a8..6f6adfbe17e0 100644 > --- a/drivers/usb/misc/Makefile > +++ b/drivers/usb/misc/Makefile > @@ -29,6 +29,7 @@ obj-$(CONFIG_USB_HUB_USB251XB) += usb251xb.o > obj-$(CONFIG_USB_HSIC_USB3503) += usb3503.o > obj-$(CONFIG_USB_HSIC_USB4604) += usb4604.o > obj-$(CONFIG_USB_CHAOSKEY) += chaoskey.o > +obj-$(CONFIG_USB_LJCA) += ljca.o > > obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/ > obj-$(CONFIG_USB_LINK_LAYER_TEST) += lvstest.o > diff --git a/drivers/usb/misc/ljca.c b/drivers/usb/misc/ljca.c > new file mode 100644 > index 000000000000..ab98deaf0074 > --- /dev/null > +++ b/drivers/usb/misc/ljca.c > @@ -0,0 +1,998 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * Intel La Jolla Cove Adapter USB driver > + * > + * Copyright (c) 2023, Intel Corporation. > + */ > + > +#include <linux/dev_printk.h> > +#include <linux/kernel.h> > +#include <linux/mfd/core.h> Please don't use the MFD API outside of drivers/mfd. If you wish to use the API, please do. Strip out (only) the MFD parts and move them into drivers/mfd. > +#include <linux/module.h> > +#include <linux/mod_devicetable.h> > +#include <linux/mutex.h> > +#include <linux/platform_device.h> > +#include <linux/slab.h> > +#include <linux/types.h> > +#include <linux/usb.h> > +#include <linux/usb/ljca.h> -- Lee Jones [李琼斯]
Hi Greq, Thanks for the review. On Mon, Mar 13, 2023 at 07:56:50AM +0100, Greg Kroah-Hartman wrote: > On Mon, Mar 13, 2023 at 03:04:31AM +0800, Ye Xiang wrote: > > This patch implements the USB part of Intel USB-I2C/GPIO/SPI adapter > > device named "La Jolla Cove Adapter" (LJCA). > > > > The communication between the various LJCA module drivers and the > > hardware will be muxed/demuxed by this driver. The sub-module of > > LJCA can use ljca_transfer() to issue a transfer between host > > and hardware. > > So you have 2 different things happening in this driver. One is the USB > interaction and control and stuff, and one is the creation of an api > that is to be used by other parts of the kernel. > > Can you split this up into 2 different commits, one for the api, and one > for the USB stuff? I think you will find that the API is going to need > a bunch of work, as it's not "normal" for what the kernel is expecting > to have. Thanks. I will split this into to commits on v6. > > Some other review comments below: > > > +struct ljca_event_cb_entry { > > + ljca_event_cb_t notify; > > + void *context; > > Why a void *? We use `void *` here because we don't know the data type of context on this driver. The `context` here is the first parameter of `notify` callback. And the `notify` callback is registered by sub-modules. So it represents the execution context of whom registered this callback and the type of it is determined by who registered the callback. > > > +}; > > + > > +struct ljca_dev { > > + struct usb_device *udev; > > + struct usb_interface *intf; > > If you have the interface, you can get the usb device. Why store both? okay, I can just store intf here. When udev are needed when submitting a urb, `interface_to_usbdev` can be used to get the udev. > > > + u8 in_ep; /* the address of the bulk in endpoint */ > > + u8 out_ep; /* the address of the bulk out endpoint */ > > + > > + /* the urb/buffer for read */ > > + struct urb *in_urb; > > + unsigned char *ibuf; > > + size_t ibuf_len; > > + > > + bool started; > > You can't just use a boolean as a "flag" without any locking, that is > not going to work, sorry. ok, I will use a mutex to protect it. > > > + struct list_head stubs_list; > > + > > + /* to wait for an ongoing write ack */ > > + wait_queue_head_t ack_wq; > > + > > + struct mfd_cell *cells; > > + int cell_count; > > + /* mutex to protect package transfer with LJCA device */ > > + struct mutex mutex; > > Why is this not protecting "started" as well as the other fields in this > structure? Ok, will use it to protect "started", and other fields if needed. > > > +}; > > + > > +struct ljca { > > + u8 type; > > + struct ljca_dev *dev; > > +}; > > + > > +struct ljca_stub_packet { > > + unsigned int *ibuf_len; > > + u8 *ibuf; > > +}; > > + > > +struct ljca_stub { > > + struct list_head list; > > + struct usb_interface *intf; > > + struct ljca_stub_packet ipacket; > > + u8 type; > > + > > + /* for identify ack */ > > + bool acked; > > + int cur_cmd; > > + > > + struct ljca_event_cb_entry event_entry; > > + /* lock to protect event_entry */ > > + spinlock_t event_cb_lock; > > + > > + struct ljca ljca; > > + unsigned long priv[]; > > What is "priv" for? Why is it unsigned long and not a real type? The priv type is different for each stub. So it can be several types and we use unsigned long type instead. > > > +}; > > + > > +static inline void *ljca_priv(struct ljca_stub *stub) > > +{ > > + return stub->priv; > > Why is this a void *? return void here can save one type casting when using `priv = ljca_priv(stub);` > > > > +} > > + > > +static bool ljca_validate(struct ljca_msg *header, u32 data_len) > > +{ > > + return header->len + sizeof(*header) == data_len; > > +} > > + > > +static struct ljca_stub *ljca_stub_alloc(struct ljca_dev *dev, u8 type, int priv_size) > > +{ > > + struct ljca_stub *stub; > > + > > + stub = kzalloc(struct_size(stub, priv, priv_size), GFP_KERNEL); > > + if (!stub) > > + return ERR_PTR(-ENOMEM); > > + > > + stub->type = type; > > + stub->intf = dev->intf; > > + stub->ljca.dev = dev; > > You are saving a reference to a reference counted device, yet never > grabbing the reference? That is ripe for disaster. ok, will use usb_get_intf to increment reference count. > > > + stub->ljca.type = stub->type; > > + spin_lock_init(&stub->event_cb_lock); > > + list_add_tail(&stub->list, &dev->stubs_list); > > Where is the reference counting on this new structure that you just > created? Who controls the lifespan of it? We didn't use reference counting for this `struct ljca_stub` structure. The stubs will be destroyed in ljca_stub_cleanup. So The life span of stubs is from create to ljca_disconnect->ljca_stub_cleanup. > > > + return stub; > > +} > > + > > +static struct ljca_stub *ljca_stub_find(struct ljca_dev *dev, u8 type) > > +{ > > + struct ljca_stub *stub; > > + > > + list_for_each_entry(stub, &dev->stubs_list, list) { > > + if (stub->type == type) > > + return stub; > > + } > > + > > + dev_err(&dev->intf->dev, "USB stub not found, type:%d\n", type); > > + > > + return ERR_PTR(-ENODEV); > > +} > > + > > +static void ljca_stub_notify(struct ljca_stub *stub, u8 cmd, const void *evt_data, int len) > > +{ > > + unsigned long flags; > > + > > + spin_lock_irqsave(&stub->event_cb_lock, flags); > > + if (stub->event_entry.notify && stub->event_entry.context) > > + stub->event_entry.notify(stub->event_entry.context, cmd, evt_data, len); > > + spin_unlock_irqrestore(&stub->event_cb_lock, flags); > > +} > > + > > +static int ljca_parse(struct ljca_dev *dev, struct ljca_msg *header) > > +{ > > + struct ljca_stub *stub; > > + > > + stub = ljca_stub_find(dev, header->type); > > + if (IS_ERR(stub)) > > + return PTR_ERR(stub); > > + > > + if (!(header->flags & LJCA_ACK_FLAG)) { > > + ljca_stub_notify(stub, header->cmd, header->data, header->len); > > + return 0; > > + } > > + > > + if (stub->cur_cmd != header->cmd) { > > + dev_err(&dev->intf->dev, "header and stub current command mismatch (%x vs %x)\n", > > + header->cmd, stub->cur_cmd); > > + return -EINVAL; > > + } > > + > > + if (stub->ipacket.ibuf && stub->ipacket.ibuf_len) { > > + unsigned int newlen; > > + > > + newlen = min_t(unsigned int, header->len, *stub->ipacket.ibuf_len); > > + > > + *stub->ipacket.ibuf_len = newlen; > > + memcpy(stub->ipacket.ibuf, header->data, newlen); > > + } > > + > > + stub->acked = true; > > + wake_up(&dev->ack_wq); > > + > > + return 0; > > +} > > + > > +static int ljca_stub_write(struct ljca_stub *stub, u8 cmd, const void *obuf, unsigned int obuf_len, > > Why is obuf a void *? It's a real structure (or u8 stream), make it so. obuf and ibuf here is the transfer payload of LJCA. It has several types for different packages (such as gpio package, i2c package andspi package). If change the type to u8, we need to add a type casting from real package type to u8 everywhere when calling ljca_transfer. > > > + void *ibuf, unsigned int *ibuf_len, bool wait_ack, unsigned long timeout) > > Same for ibuf. > > > +{ > > + struct ljca_dev *dev = usb_get_intfdata(stub->intf); > > + u8 flags = LJCA_CMPL_FLAG; > > + struct ljca_msg *header; > > + unsigned int msg_len = sizeof(*header) + obuf_len; > > + int actual; > > + int ret; > > + > > + if (msg_len > LJCA_MAX_PACKET_SIZE) > > + return -EINVAL; > > + > > + if (wait_ack) > > + flags |= LJCA_ACK_FLAG; > > + > > + header = kmalloc(msg_len, GFP_KERNEL); > > + if (!header) > > + return -ENOMEM; > > + > > + header->type = stub->type; > > + header->cmd = cmd; > > + header->flags = flags; > > + header->len = obuf_len; > > + > > + if (obuf) > > + memcpy(header->data, obuf, obuf_len); > > + > > + dev_dbg(&dev->intf->dev, "send: type:%d cmd:%d flags:%d len:%d\n", header->type, > > + header->cmd, header->flags, header->len); > > + > > + usb_autopm_get_interface(dev->intf); > > + if (!dev->started) { > > + kfree(header); > > + ret = -ENODEV; > > + goto error_put; > > + } > > + > > + mutex_lock(&dev->mutex); > > + stub->cur_cmd = cmd; > > + stub->ipacket.ibuf = ibuf; > > + stub->ipacket.ibuf_len = ibuf_len; > > + stub->acked = false; > > + ret = usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, dev->out_ep), header, msg_len, > > + &actual, LJCA_USB_WRITE_TIMEOUT_MS); > > + kfree(header); > > + if (ret) { > > + dev_err(&dev->intf->dev, "bridge write failed ret:%d\n", ret); > > + goto error_unlock; > > + } > > + > > + if (actual != msg_len) { > > + dev_err(&dev->intf->dev, "bridge write length mismatch (%d vs %d)\n", msg_len, > > + actual); > > + ret = -EINVAL; > > + goto error_unlock; > > + } > > + > > + if (wait_ack) { > > + ret = wait_event_timeout(dev->ack_wq, stub->acked, msecs_to_jiffies(timeout)); > > + if (!ret) { > > + dev_err(&dev->intf->dev, "acked wait timeout\n"); > > + ret = -ETIMEDOUT; > > + goto error_unlock; > > + } > > + } > > + > > + stub->ipacket.ibuf = NULL; > > + stub->ipacket.ibuf_len = NULL; > > + ret = 0; > > +error_unlock: > > + mutex_unlock(&dev->mutex); > > +error_put: > > + usb_autopm_put_interface(dev->intf); > > + > > + return ret; > > +} > > + > > +static int ljca_transfer_internal(struct ljca *ljca, u8 cmd, const void *obuf, > > + unsigned int obuf_len, void *ibuf, unsigned int *ibuf_len, > > + bool wait_ack) > > +{ > > + struct ljca_stub *stub; > > + > > + stub = ljca_stub_find(ljca->dev, ljca->type); > > + if (IS_ERR(stub)) > > + return PTR_ERR(stub); > > + > > + return ljca_stub_write(stub, cmd, obuf, obuf_len, ibuf, ibuf_len, wait_ack, > > + LJCA_USB_WRITE_ACK_TIMEOUT_MS); > > +} > > + > > +int ljca_transfer(struct ljca *ljca, u8 cmd, const void *obuf, unsigned int obuf_len, void *ibuf, > > + unsigned int *ibuf_len) > > +{ > > + return ljca_transfer_internal(ljca, cmd, obuf, obuf_len, ibuf, ibuf_len, true); > > +} > > +EXPORT_SYMBOL_NS_GPL(ljca_transfer, LJCA); > > + > > +int ljca_transfer_noack(struct ljca *ljca, u8 cmd, const void *obuf, unsigned int obuf_len) > > +{ > > + return ljca_transfer_internal(ljca, cmd, obuf, obuf_len, NULL, NULL, false); > > +} > > +EXPORT_SYMBOL_NS_GPL(ljca_transfer_noack, LJCA); > > + > > +int ljca_register_event_cb(struct ljca *ljca, ljca_event_cb_t event_cb, void *context) > > What are these magic events you are registering? What do they do and > why would anyone need them? It provides a path for sub-devices to listening events from LJCA device. For gpio-ljca, it uses this API to implement gpio interrupt. > > You have global functions here that other drivers are using, yet no > documentation about them at all for any way to review that the api > really is doing what you are wanting it to do. Kernel-doc format comments are in ljca.h in this patch. It has some introduction about these for exported API. > > So again, please split this up into at least 2 changes, and document > this new api you are creating, so that we have a chance to review it > properly. Otherwise it's almost impossible to do so. Got it. Will spit this into 2 commit on next version. > -- Thanks Ye Xiang
Hi Andi, Thanks for the review. On Mon, Mar 13, 2023 at 05:21:46PM +0100, Andi Shyti wrote: > Hi Ye, > > On top of what Greg has already said, few things from my side > through the lines. > > [...] > > > +static int ljca_mng_link(struct ljca_dev *dev, struct ljca_stub *stub) > > +{ > > + int ret; > > + > > + ret = ljca_mng_reset_handshake(stub); > > + if (ret) > > + return ret; > > + > > + /* try to enum all the stubs */ > > + ljca_mng_enum_gpio(stub); > > + ljca_mng_enum_i2c(stub); > > + ljca_mng_enum_spi(stub); > > We are ignoring here the return value. So either make the > whole function call chain to be void or please check the return > values here. Ok, got it. > > [...] > > > +static ssize_t ljca_enable_dfu_store(struct device *dev, struct device_attribute *attr, > > + const char *buf, size_t count) > > +{ > > + struct usb_interface *intf = to_usb_interface(dev); > > + struct ljca_dev *ljca_dev = usb_get_intfdata(intf); > > + struct ljca_stub *mng_stub = ljca_stub_find(ljca_dev, LJCA_MNG_STUB); > > + bool enable; > > + int ret; > > + > > + ret = kstrtobool(buf, &enable); > > + if (ret) > > + return ret; > > + > > + if (enable) { > > + ret = ljca_mng_set_dfu_mode(mng_stub); > > + if (ret) > > + return ret; > > + } > > What is the DFU mode? > Is it an operational mode? > Do we enter and exit from it? > Does the device leave this mode on its own? > What if I write twice in a raw enable? > Can I check if I am in DFU mode or not? > > Would you mind adding some comments here? DFU mode is used for updating LJCA device Firmware. We have a Documentation/ABI/testing/sysfs-bus-usb-devices-ljca in patch 5 of this patch series. It has information about the entry. Maybe it should be go after this patch (patch 2/5). > > > + > > + return count; > > +} > > +static DEVICE_ATTR_WO(ljca_enable_dfu); > > + > > +static ssize_t ljca_trace_level_store(struct device *dev, struct device_attribute *attr, > > + const char *buf, size_t count) > > +{ > > + struct usb_interface *intf = to_usb_interface(dev); > > + struct ljca_dev *ljca_dev = usb_get_intfdata(intf); > > + struct ljca_stub *diag_stub = ljca_stub_find(ljca_dev, LJCA_DIAG_STUB); > > + u8 level; > > + int ret; > > + > > + if (kstrtou8(buf, 0, &level)) > > + return -EINVAL; > > + > > + ret = ljca_diag_set_trace_level(diag_stub, level); > > + if (ret) > > + return ret; > > do we need any range check for the levels? What happens if I do: > > echo "I am too cool" > /sys/.../ljca_trace_level > > As there were questions here, would you mind adding some comments > so that the next reader won't make the same questions? We have a patch (patch 5 of this series) adding some Documentation/ABI/ entries to specify the correct value for each entry. > > > + > > + return count; > > +} > > +static DEVICE_ATTR_WO(ljca_trace_level); > > [...] > > > +static int ljca_probe(struct usb_interface *intf, const struct usb_device_id *id) > > +{ > > + struct ljca_dev *dev; > > + struct usb_endpoint_descriptor *bulk_in, *bulk_out; > > + int ret; > > + > > + /* allocate memory for our device state and initialize it */ > > + dev = kzalloc(sizeof(*dev), GFP_KERNEL); > > devm_kzalloc() Got it. Thanks. -- Thanks Ye Xiang
Hi Lee, Thanks for the review. On Mon, Mar 13, 2023 at 05:03:41PM +0000, Lee Jones wrote: > On Mon, 13 Mar 2023, Ye Xiang wrote: > > > This patch implements the USB part of Intel USB-I2C/GPIO/SPI adapter > > device named "La Jolla Cove Adapter" (LJCA). > > > > The communication between the various LJCA module drivers and the > > hardware will be muxed/demuxed by this driver. The sub-module of > > LJCA can use ljca_transfer() to issue a transfer between host > > and hardware. > > > > Each sub-module of LJCA device is identified by type field within > > the LJCA message header. > > > > The minimum code in ASL that covers this board is > > Scope (\_SB.PCI0.DWC3.RHUB.HS01) > > { > > Device (GPIO) > > { > > Name (_ADR, Zero) > > Name (_STA, 0x0F) > > } > > > > Device (I2C) > > { > > Name (_ADR, One) > > Name (_STA, 0x0F) > > } > > > > Device (SPI) > > { > > Name (_ADR, 0x02) > > Name (_STA, 0x0F) > > } > > } > > > > Signed-off-by: Ye Xiang <xiang.ye@intel.com> > > Reviewed-by: Sakari Ailus <sakari.ailus@linux.intel.com> > > --- > > drivers/usb/misc/Kconfig | 13 + > > drivers/usb/misc/Makefile | 1 + > > drivers/usb/misc/ljca.c | 998 ++++++++++++++++++++++++++++++++++++++ > > include/linux/usb/ljca.h | 95 ++++ > > 4 files changed, 1107 insertions(+) > > create mode 100644 drivers/usb/misc/ljca.c > > create mode 100644 include/linux/usb/ljca.h > > > > diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig > > index a5f7652db7da..59ec120c26d4 100644 > > --- a/drivers/usb/misc/Kconfig > > +++ b/drivers/usb/misc/Kconfig > > @@ -273,6 +273,19 @@ config USB_LINK_LAYER_TEST > > Layer Test Device. Say Y only when you want to conduct USB Super Speed > > Link Layer Test for host controllers. > > > > +config USB_LJCA > > + tristate "Intel La Jolla Cove Adapter support" > > + select MFD_CORE > > + depends on USB > > + help > > + This adds support for Intel La Jolla Cove USB-I2C/SPI/GPIO > > + Master Adapter (LJCA). Additional drivers such as I2C_LJCA, > > + GPIO_LJCA and SPI_LJCA must be enabled in order to use the > > + functionality of the device. > > + > > + This driver can also be built as a module. If so, the module > > + will be called ljca. > > + > > config USB_CHAOSKEY > > tristate "ChaosKey random number generator driver support" > > depends on HW_RANDOM > > diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile > > index 93581baec3a8..6f6adfbe17e0 100644 > > --- a/drivers/usb/misc/Makefile > > +++ b/drivers/usb/misc/Makefile > > @@ -29,6 +29,7 @@ obj-$(CONFIG_USB_HUB_USB251XB) += usb251xb.o > > obj-$(CONFIG_USB_HSIC_USB3503) += usb3503.o > > obj-$(CONFIG_USB_HSIC_USB4604) += usb4604.o > > obj-$(CONFIG_USB_CHAOSKEY) += chaoskey.o > > +obj-$(CONFIG_USB_LJCA) += ljca.o > > > > obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/ > > obj-$(CONFIG_USB_LINK_LAYER_TEST) += lvstest.o > > diff --git a/drivers/usb/misc/ljca.c b/drivers/usb/misc/ljca.c > > new file mode 100644 > > index 000000000000..ab98deaf0074 > > --- /dev/null > > +++ b/drivers/usb/misc/ljca.c > > @@ -0,0 +1,998 @@ > > +// SPDX-License-Identifier: GPL-2.0-only > > +/* > > + * Intel La Jolla Cove Adapter USB driver > > + * > > + * Copyright (c) 2023, Intel Corporation. > > + */ > > + > > +#include <linux/dev_printk.h> > > +#include <linux/kernel.h> > > +#include <linux/mfd/core.h> > > Please don't use the MFD API outside of drivers/mfd. > > If you wish to use the API, please do. > > Strip out (only) the MFD parts and move them into drivers/mfd. I have no idea about how to split MFD parts out from this driver currently. The MFD part just have mfd cells filling and the call mfd_add_hotplug_devices to register mfd devices. How to module them as an independent driver? Would you give some hints or recommendations? And I am a little comfused about where this USB device driver should be put to (drivers/mfd or drivers/usb). As far as I know, where a driver should be put is based on what it provides. This driver just do some urb package transfer to provides multi-functions, such as GPIO function, I2C function, SPI function. so it should be under drivers/mfd folder. Please correct me, if something is wrong. Thanks > > > +#include <linux/module.h> > > +#include <linux/mod_devicetable.h> > > +#include <linux/mutex.h> > > +#include <linux/platform_device.h> > > +#include <linux/slab.h> > > +#include <linux/types.h> > > +#include <linux/usb.h> > > +#include <linux/usb/ljca.h> > -- Thanks Ye Xiang
Hi Xiang, On Tue, Mar 14, 2023 at 04:03:26PM +0800, Ye, Xiang wrote: > > Please don't use the MFD API outside of drivers/mfd. > > > > If you wish to use the API, please do. > > > > Strip out (only) the MFD parts and move them into drivers/mfd. > I have no idea about how to split MFD parts out from this driver > currently. The MFD part just have mfd cells filling and the call > mfd_add_hotplug_devices to register mfd devices. How to module them > as an independent driver? > Would you give some hints or recommendations? > > And I am a little comfused about where this USB device driver should > be put to (drivers/mfd or drivers/usb). > > As far as I know, where a driver should be put is based on what > it provides. This driver just do some urb package transfer to provides > multi-functions, such as GPIO function, I2C function, SPI function. > so it should be under drivers/mfd folder. Please correct me, if > something is wrong. Thanks You don't really seem to get any benefit from MFD. Perhaps it would be more appropriate and clear if you just registered auxiliary devices in this driver. Check drivers/base/auxiliary.c. thanks,
Hi Heikki, Thanks for the review. On Tue, Mar 14, 2023 at 10:36:57AM +0200, Heikki Krogerus wrote: > Hi Xiang, > > On Tue, Mar 14, 2023 at 04:03:26PM +0800, Ye, Xiang wrote: > > > Please don't use the MFD API outside of drivers/mfd. > > > > > > If you wish to use the API, please do. > > > > > > Strip out (only) the MFD parts and move them into drivers/mfd. > > I have no idea about how to split MFD parts out from this driver > > currently. The MFD part just have mfd cells filling and the call > > mfd_add_hotplug_devices to register mfd devices. How to module them > > as an independent driver? > > Would you give some hints or recommendations? > > > > And I am a little comfused about where this USB device driver should > > be put to (drivers/mfd or drivers/usb). > > > > As far as I know, where a driver should be put is based on what > > it provides. This driver just do some urb package transfer to provides > > multi-functions, such as GPIO function, I2C function, SPI function. > > so it should be under drivers/mfd folder. Please correct me, if > > something is wrong. Thanks > > You don't really seem to get any benefit from MFD. Perhaps it would be > more appropriate and clear if you just registered auxiliary devices in > this driver. Check drivers/base/auxiliary.c. Yes, it should be a work. I have a question. MFD provides the ACPI binding for sub-devices through struct mfd_cell_acpi_match. But I didn't see this in drivers/base/auxiliary.c. If using auxiliary bus to implement the LJCA sub-devices, we need to do the sub-devices acpi binding manually in ljca.c. Something Like: adr = LJCA_ACPI_MATCH_GPIO adev = acpi_find_child_device(parent, adr, false); ACPI_COMPANION_SET(&pdev->dev, adev ?: parent); Is that acceptable? -- Thanks Ye Xiang
On Tue, Mar 14, 2023 at 11:38:14PM +0800, Ye, Xiang wrote: > On Tue, Mar 14, 2023 at 10:36:57AM +0200, Heikki Krogerus wrote: > > On Tue, Mar 14, 2023 at 04:03:26PM +0800, Ye, Xiang wrote: ... > > You don't really seem to get any benefit from MFD. Perhaps it would be > > more appropriate and clear if you just registered auxiliary devices in > > this driver. Check drivers/base/auxiliary.c. > Yes, it should be a work. I have a question. > MFD provides the ACPI binding for sub-devices through > struct mfd_cell_acpi_match. But I didn't see this in drivers/base/auxiliary.c. > If using auxiliary bus to implement the LJCA sub-devices, we need to do > the sub-devices acpi binding manually in ljca.c. > > Something Like: > adr = LJCA_ACPI_MATCH_GPIO > adev = acpi_find_child_device(parent, adr, false); > ACPI_COMPANION_SET(&pdev->dev, adev ?: parent); > > Is that acceptable? Maybe you can implement this on the level of auxiliary bus.
On Tue, Mar 14, 2023 at 05:52:52PM +0200, Andy Shevchenko wrote: > On Tue, Mar 14, 2023 at 11:38:14PM +0800, Ye, Xiang wrote: > > On Tue, Mar 14, 2023 at 10:36:57AM +0200, Heikki Krogerus wrote: > > > On Tue, Mar 14, 2023 at 04:03:26PM +0800, Ye, Xiang wrote: > > ... > > > > You don't really seem to get any benefit from MFD. Perhaps it would be > > > more appropriate and clear if you just registered auxiliary devices in > > > this driver. Check drivers/base/auxiliary.c. > > Yes, it should be a work. I have a question. > > MFD provides the ACPI binding for sub-devices through > > struct mfd_cell_acpi_match. But I didn't see this in drivers/base/auxiliary.c. > > If using auxiliary bus to implement the LJCA sub-devices, we need to do > > the sub-devices acpi binding manually in ljca.c. > > > > Something Like: > > adr = LJCA_ACPI_MATCH_GPIO > > adev = acpi_find_child_device(parent, adr, false); > > ACPI_COMPANION_SET(&pdev->dev, adev ?: parent); > > > > Is that acceptable? Looks ok to me. > Maybe you can implement this on the level of auxiliary bus. I would actually prefer that the auxiliary bus itself does not make any assumptions regarding the whereabouts of the fwnodes at this stage. Maybe later, when(if) there are more users. thanks,