usb: misc: xapea00x: add driver for Xaptum ENF Access Card
PATCH 1/2 usb: misc: xapea00x: add driver for Xaptum ENF Access Card
Am Mittwoch, den 10.01.2018, 10:58 -0600 schrieb David R. Bild :
Quoted 127 lines
This commit adds a driver for the Xaptum ENF Access Card, a TPM2.0 hardware module for authenticating IoT devices and gateways.
The card consists of a SPI TPM 2.0 chip and a USB-SPI bridge. This driver configures the bridge, registers the bridge as an SPI controller, and adds the TPM 2.0 as an SPI device. The in-kernel TPM 2.0 driver is then automatically loaded to configure the TPM and expose it to userspace.
Signed-off-by David R. Bild <[email protected]>--- MAINTAINERS | 6 + drivers/usb/misc/Kconfig | 2 + drivers/usb/misc/Makefile | 1 + drivers/usb/misc/xapea00x/Kconfig | 16 ++ drivers/usb/misc/xapea00x/Makefile | 7 + drivers/usb/misc/xapea00x/xapea00x-bridge.c | 399 ++++++++++++++++++++++++++ drivers/usb/misc/xapea00x/xapea00x-core.c | 429 ++++++++++++++++++++++++++++ drivers/usb/misc/xapea00x/xapea00x-spi.c | 209 ++++++++++++++ drivers/usb/misc/xapea00x/xapea00x.h | 75 +++++ 9 files changed, 1144 insertions(+) create mode 100644 drivers/usb/misc/xapea00x/Kconfig create mode 100644 drivers/usb/misc/xapea00x/Makefile create mode 100644 drivers/usb/misc/xapea00x/xapea00x-bridge.c create mode 100644 drivers/usb/misc/xapea00x/xapea00x-core.c create mode 100644 drivers/usb/misc/xapea00x/xapea00x-spi.c create mode 100644 drivers/usb/misc/xapea00x/xapea00x.hMAINTAINERS +6
-0@@ -14300,6 +14300,12 @@ L: [email protected] S: Maintained F: drivers/net/wireless/rndis_wlan.c +USB XAPEA00X DRIVER +M: David R. Bild <[email protected]> +L: [email protected] +S: Maintained +F: drivers/usb/misc/xapea00x/ + USB XHCI DRIVER M: Mathias Nyman <[email protected]> L: [email protected]drivers/usb/misc/Kconfig +2
-0@@ -275,3 +275,5 @@ config USB_CHAOSKEY To compile this driver as a module, choose M here: the module will be called chaoskey. + +source "drivers/usb/misc/xapea00x/Kconfig"drivers/usb/misc/Makefile +1
-0@@ -30,4 +30,5 @@ obj-$(CONFIG_USB_HSIC_USB4604) += usb4604.o obj-$(CONFIG_USB_CHAOSKEY) += chaoskey.o obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/ +obj-$(CONFIG_USB_XAPEA00X) += xapea00x/ obj-$(CONFIG_USB_LINK_LAYER_TEST) += lvstest.odrivers/usb/misc/xapea00x/Kconfig new file +16
-0@@ -0,0 +1,16 @@ +config USB_XAPEA00X + tristate "Xaptum ENF Access card support (XAP-EA-00x)" + depends on USB_SUPPORT + select SPI + select TCG_TPM + select TCG_TIS_SPI + ---help--- + Say Y here if you want to support the Xaptum ENF Access + modules (XAP-EA-00x) in the USB or Mini PCI-e form + factors. The XAP-EA-00x module exposes a TPM 2.0 as + /dev/tpmX to use for authenticating with the Xaptum ENF. + + To compile this driver as a module, choose M here. The + module will be called xapea00x. + + If unsure, say M.drivers/usb/misc/xapea00x/Makefile new file +7
-0@@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the xapea00x driver. +# +obj-$(CONFIG_USB_XAPEA00X) += xapea00x.o + +xapea00x-y += xapea00x-core.o xapea00x-bridge.odrivers/usb/misc/xapea00x/xapea00x-bridge.c new file +21
-0@@ -0,0 +1,399 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* +* Driver for the XAP-EA-00x series of the Xaptum Edge Access Card, a +* TPM 2.0-based hardware module for authenticating IoT devices and +* gateways. +* +* Copyright (c) 2017 Xaptum, Inc. +*/ + +#include "xapea00x.h" + +#define XAPEA00X_BR_CMD_READ 0x00 +#define XAPEA00X_BR_CMD_WRITE 0x01 +#define XAPEA00X_BR_CMD_WRITEREAD 0x02 + +#define XAPEA00X_BR_BREQTYP_SET 0x40 + +#define XAPEA00X_BR_BREQ_SET_GPIO_VALUES 0x21 +#define XAPEA00X_BR_BREQ_SET_GPIO_CS 0x25 +#define XAPEA00X_BR_BREQ_SET_SPI_WORD 0x31 +
drivers/usb/misc/xapea00x/xapea00x-bridge.c line 31
Quoted 10 lines
drivers/usb/misc/xapea00x/xapea00x-bridge.c new file +10
-0+#define XAPEA00X_BR_USB_TIMEOUT 1000 // msecs + +#define XAPEA00X_BR_CS_DISABLED 0x00 + +/******************************************************************************* +* Bridge USB transfers +*/ + +struct xapea00x_br_bulk_command { +__be16 reserved1;
Here BE ...
drivers/usb/misc/xapea00x/xapea00x-bridge.c line 34
Quoted 3 lines
drivers/usb/misc/xapea00x/xapea00x-bridge.c new file +3
-0+__u8 command; +__u8 reserved2; +__le32 length;
... there LE?
Quoted 14 lines
drivers/usb/misc/xapea00x/xapea00x-bridge.c new file +14
-0+} __attribute__((__packed__)); + +/** +* xapea00x_br_prep_bulk_command - Prepares the bulk command header with +* the supplied values. +* @hdr: pointer to header to prepare +* @command: the command id for the command +* @length: length in bytes of the command data +* +* Context: !in_interrupt() +* +* Return: If successful, 0. Otherwise a negative error number. +*/ +static void xapea00x_br_prep_bulk_command(struct xapea00x_br_bulk_command *hdr,
drivers/usb/misc/xapea00x/xapea00x-bridge.c line 58
Quoted 10 lines
drivers/usb/misc/xapea00x/xapea00x-bridge.c new file +10
-0+ u8 command, int length) +{ + hdr->reserved1 =0 ; + hdr->command = command; + hdr->reserved2 =0 ; + hdr->length =__cpu_to_le32 (length); +} + +/** +* xapea00x_br_bulk_write - Issues a builk write to the bridge chip.
typo
Quoted 10 lines
drivers/usb/misc/xapea00x/xapea00x-bridge.c new file +10
-0+* @dev: pointer to the device to write to +* @command: the command started by this write (WRITE, READ, WRITE_READ) +* @data: pointer to the data to write. Must be DMA capable (e.g., +* kmalloc-ed, not stack). +* @len: length in bytes of the data to write +* +* Context: !in_interrupt() +* +* Return: If successful, 0. Otherwise a negative error number. +*/
drivers/usb/misc/xapea00x/xapea00x-bridge.c line 78
Quoted 10 lines
drivers/usb/misc/xapea00x/xapea00x-bridge.c new file +10
-0+static int xapea00x_br_bulk_write (struct xapea00x_device *dev, +struct xapea00x_br_bulk_command *header, +const void *data,int len) +{ +u8 *buf; +unsigned int pipe; +int buf_len, actual_len, retval; + + buf_len =sizeof (struct xapea00x_br_bulk_command ) + len; + buf =kzalloc (buf_len,GFP_KERNEL );
Why zeroed? You copy all over it.
Quoted 6 lines
drivers/usb/misc/xapea00x/xapea00x-bridge.c new file +6
-0+if (!buf) { + retval = -ENOMEM ; + goto out; + } + +memcpy (buf, header,sizeof (struct xapea00x_br_bulk_command ));
drivers/usb/misc/xapea00x/xapea00x-bridge.c line 94
Quoted 10 lines
drivers/usb/misc/xapea00x/xapea00x-bridge.c new file +10
-0+memcpy (buf +sizeof (struct xapea00x_br_bulk_command ), data, len); + + pipe =usb_sndbulkpipe (dev->udev, dev->bulk_out->bEndpointAddress); + retval =usb_bulk_msg (dev->udev, pipe, buf, buf_len, &actual_len, +XAPEA00X_BR_USB_TIMEOUT ); +if (retval) { +dev_warn (&dev->interface->dev, +"%s: usb_bulk_msg() failed with %d\n" , + __func__, retval); + goto free_buf;
Does this make any sense?
Quoted 63 lines
drivers/usb/misc/xapea00x/xapea00x-bridge.c new file +63
-0+ } + + retval =0 ; + +free_buf: +kzfree (buf); + +out: +return retval; +} + +/** +* xapea00x_br_bulk_read - Issues a bulk read to the bridge chip. +* @dev: pointer to the device to read from +* @data: pointer to the data read. Must be DMA capable (e.g., +* kmalloc-ed, not stack). +* @len: length in bytes of the data to read +* +* Return: If successful, 0. Otherwise a negative error code. +*/ +static int xapea00x_br_bulk_read (struct xapea00x_device *dev,void *data, +int len) +{ +unsigned int pipe; +void *buf; +int actual_len, retval; + + buf =kzalloc (len,GFP_KERNEL ); +if (!buf) { + retval = -ENOMEM ; + goto out; + } + + pipe =usb_rcvbulkpipe (dev->udev, dev->bulk_in->bEndpointAddress); + retval =usb_bulk_msg (dev->udev, pipe, buf, len, &actual_len, +XAPEA00X_BR_USB_TIMEOUT ); + +if (retval) { +dev_warn (&dev->interface->dev, +"%s: usb_bulk_msg() failed with %d\n" , + __func__, retval); + goto free_buf; + } + +memcpy (data, buf, actual_len); + retval =0 ; + +free_buf: +kzfree (buf); + +out: +return retval; +} + +/** +* xapea00x_br_ctrl_write - Issues a send control transfer to the bridge +* chip. +* @dev: pointer to the device to write to +* @bRequest: the command +* @wValue: the command value +* @wIndex: the command index +* @data: pointer to the command data +* @len: length in bytes of the command data
drivers/usb/misc/xapea00x/xapea00x-bridge.c line 167
Quoted 10 lines
drivers/usb/misc/xapea00x/xapea00x-bridge.c new file +10
-0+* +* The possible bRequest, wValue, and wIndex values and data format +* are specified in the hardware datasheet. +* +* Context: !in_interrupt() +* +* Return: If successful, 0. Otherwise a negative error code. +*/ +static int xapea00x_br_ctrl_write (struct xapea00x_device *dev,u8 bRequest, +u16 wValue,u16 wIndex,u8 *data,u16 len)
Combine with xapea00x_br_bulk_write()?
drivers/usb/misc/xapea00x/xapea00x-bridge.c line 179
Quoted 12 lines
drivers/usb/misc/xapea00x/xapea00x-bridge.c new file +12
-0+{ +unsigned int pipe; +void *buf; +int retval; + +/* control_msg buffer must be dma-capable (e.g.k kmalloc-ed, +* not stack). Copy data into such buffer here, so we can use +* simpler stack allocation in the callers - we have no +* performance concerns given the small buffers and low +* throughputs of this device. +*/ + buf =kzalloc (len,GFP_KERNEL );
Why kzalloc something you copy over as the very next thing?
Quoted 55 lines
drivers/usb/misc/xapea00x/xapea00x-bridge.c new file +55
-0+if (!buf) { + retval = -ENOMEM ; + goto out; + } +memcpy (buf, data, len); + + pipe =usb_sndctrlpipe (dev->udev,0 ); + retval =usb_control_msg (dev->udev, pipe, bRequest, +XAPEA00X_BR_BREQTYP_SET , wValue, wIndex, + buf, len,XAPEA00X_BR_USB_TIMEOUT ); +if (retval <0 ) { +dev_warn (&dev->interface->dev, +"usb_control_msg() failed with %d\n" , retval); + goto free_buf; + } + + retval =0 ; + +free_buf: +kzfree (buf); + +out: +return retval; +} + +/******************************************************************************* +* Bridge configuration commands +*/ + +/** +* xapea00x_br_set_gpio_value - Sets the value on the specified pin of +* the bridge chip. +* @dev: pointer to the device containing the bridge whose pin to set +* @pin: the number of the pin to set +* @value: the value to set the pin to, 0 or 1 +* +* Context: !in_interrupt() +* +* Return: If successful, 0. Otherwise a negative error number. +*/ +static int xapea00x_br_set_gpio_value (struct xapea00x_device *dev,u8 pin, +u8 value) +{ +u8 data[4 ] = {0 ,0 ,0 ,0 }; + +switch (pin) { +case 10 : +case 9 : +case 8 : +case 7 : +case 6 : + data[0 ] = value << (pin -4 ); + data[2 ] =1 << (pin -4 ); +break ; +case 5 :
drivers/usb/misc/xapea00x/xapea00x-bridge.c line 244
Quoted 10 lines
drivers/usb/misc/xapea00x/xapea00x-bridge.c new file +10
-0+ data[0 ] = value; + data[2 ] =1 ; +break ; +case 4 : +case 3 : +case 2 : +case 1 : +case 0 : + data[1 ] = value << (pin +3 ); + data[3 ] =1 << (pin +3 );
Add a break please
Quoted 220 lines
drivers/usb/misc/xapea00x/xapea00x-bridge.c new file +155
-0+ } +return xapea00x_br_ctrl_write (dev,XAPEA00X_BR_BREQ_SET_GPIO_VALUES , +0 ,0 , data,4 ); +} + +/** +* xapea00x_br_set_gpio_cs - Sets the chip select control on the specified +* pin of the bridge chip. +* @dev: pointer to the device containing the bridge whose cs to set +* @pin: the number of the pin to set +* @control: the chip select control value for the pin, 0, 1, or 2 +* +* Context: !in_interrupt() +* +* Return: If successful, 0. Otherwise a negative error number. +*/ +static int xapea00x_br_set_gpio_cs (struct xapea00x_device *dev,u8 pin, +u8 control) +{ +u8 data[2 ] = { pin, control }; + +return xapea00x_br_ctrl_write (dev,XAPEA00X_BR_BREQ_SET_GPIO_CS , +0 ,0 , data,2 ); +} + +/******************************************************************************* +* Bridge configuration commands +*/ +/** +* xapea00x_br_disable_cs - disable the built-in chip select +* capability of the specified channel. It does not support holding +* the CS active between SPI transfers, a feature required for the +* TPM. Instead, we manually control the CS pin as a GPIO. +* @dev: pointer to the device containing the bridge whose cs to disable +* @channel: the SPI channel whose cs to disable +* +* Context: !in_interrupt() +* +* Return: If successful 0. Otherwise a negative error number. +*/ +int xapea00x_br_disable_cs (struct xapea00x_device *dev,u8 channel) +{ +return xapea00x_br_set_gpio_cs (dev, channel, +XAPEA00X_BR_CS_DISABLED ); +} + +/** +* xapea00x_br_assert_cs - assert the chip select pin for the +* specified channel. +* @dev: pointer to the device containing the bridge who cs to assert +* @channel: the SPI channel whose cs to assert +* +* Context: !in_interrupt() +* +* Return: If successful 0. Otherwise a negative error number. +*/ +int xapea00x_br_assert_cs (struct xapea00x_device *dev,u8 channel) +{ +return xapea00x_br_set_gpio_value (dev, channel,0 ); +} + +/** +* xapea00x_br_deassert_cs - deassert the chip select pin for the +* specified channel. +* @dev: pointer to the device containing the bridge who cs to deassert +* @channel: the SPI channel whose cs to deassert +* +* Context: !in_interrupt() +* +* Return: If successful 0. Otherwise a negative error number. +*/ +int xapea00x_br_deassert_cs (struct xapea00x_device *dev,u8 channel) +{ +return xapea00x_br_set_gpio_value (dev, channel,1 ); +} + +/******************************************************************************* +* Bridge SPI reads and writes +*/ +/** +* xeapea00x_spi_read - Performs a read from the active channel +* @dev: pointer to the device to perform the read +* @rx_buf: pointer to the buffer to read the data into. Must be +* DMA-capable (e.g., kmalloc-ed, not stack). +* @len: length in bytes of the data to read +* +* Context: !in_interrupt() +* +* Return: If successful, 0. Otherwise a negative error number. +*/ +int xapea00x_br_spi_read (struct xapea00x_device *dev,void *rx_buf,int len) +{ +struct xapea00x_br_bulk_command header; +int retval; + +xapea00x_br_prep_bulk_command (&header,XAPEA00X_BR_CMD_READ , len); + retval =xapea00x_br_bulk_write (dev, &header,NULL ,0 ); +if (retval) + goto out; + + retval =xapea00x_br_bulk_read (dev, rx_buf, len); + +out: +return retval; +} + +/** +*xapea00x_br_spi_write - Performs a write to the active channel +* @dev: pointer to the device to perform the write +* @tx_buf: pointer to the data to write. Must be DMA-capable (e.g., +* kmalloc-ed, not stack). +* @len: length in bytes of the data to write +*/ +int xapea00x_br_spi_write (struct xapea00x_device *dev,const void *tx_buf, +int len) +{ +struct xapea00x_br_bulk_command header; +int retval; + +xapea00x_br_prep_bulk_command (&header,XAPEA00X_BR_CMD_WRITE , len); + retval =xapea00x_br_bulk_write (dev, &header, tx_buf, len); + +return retval; +} + +/** +* xapea00x_br_spi_write_read - Performs a simultaneous write and read on +* the active channel +* @dev: pointer to the device to perform the write/read +* @tx_buf: pointer to the data to write. Must be DMA-capable (e.g., +* kmalloc-ed, not stack). +* @rx_buf: pointer to the buffer to read the data into. Must be +* DMA-capable (e.g., kmalloc-ed, not stack). +* @len: length in bytes of the data to write/read +* +* Context: !in_interrupt() +* +* Return: If successful, 0. Otherwise a negative error number. +*/ +int xapea00x_br_spi_write_read (struct xapea00x_device *dev,const void *tx_buf, +void *rx_buf,int len) +{ +struct xapea00x_br_bulk_command header; +int retval; + +xapea00x_br_prep_bulk_command (&header,XAPEA00X_BR_CMD_WRITEREAD , len); + retval =xapea00x_br_bulk_write (dev, &header, tx_buf, len); +if (retval) + goto out; + + retval =xapea00x_br_bulk_read (dev, rx_buf, len); + +out: +return retval; +}drivers/usb/misc/xapea00x/xapea00x-core.c new file +59
-0@@ -0,0 +1,429 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* +* Driver for the XAP-EA-00x series of the Xaptum Edge Access Card, a +* TPM 2.0-based hardware module for authenticating IoT devices and +* gateways. +* +* Copyright (c) 2017 Xaptum, Inc. +*/ + +#include "xapea00x.h" + +#define XAPEA00X_TPM_MODALIAS "tpm_tis_spi" + +#define kref_to_xapea00x (k) container_of(k, struct xapea00x_device, kref) + +static void xapea00x_delete (struct kref *kref) +{ +struct xapea00x_device *dev =kref_to_xapea00x (kref); + +usb_put_dev (dev->udev); +kfree (dev); +} + +/******************************************************************************* +* SPI master functions +*/ + +/** +* xapea00x_spi_setup - Setup the SPI channel for the TPM. +* +* Context: !in_interrupt() +* +* Return: If successful, 0. Otherwise a negative error number. +*/ +static int xapea00x_spi_setup (struct spi_device *spi) +{ +struct xapea00x_device *dev; +int retval; + + dev =spi_master_get_devdata (spi->master); + +mutex_lock (&dev->usb_mutex); +if (!dev->interface) { + retval = -ENODEV ; + goto out; + } + +/* Verify that this is the TPM device */ +if (spi->chip_select !=0 ) { + retval = -EINVAL ; + goto err; + } + +/* +* Disable auto chip select for the TPM channel. +* Must be done after setting the SPI parameters. +*/ + retval =xapea00x_br_disable_cs (dev,0 ); +if (retval)
drivers/usb/misc/xapea00x/xapea00x-core.c line 69
Quoted 10 lines
drivers/usb/misc/xapea00x/xapea00x-core.c new file +10
-0+ goto err; + +/* De-assert chip select for the TPM channel. */ + retval =xapea00x_br_deassert_cs (dev,0 ); +if (retval) + goto err; + +dev_dbg (&dev->interface->dev,"configured spi channel for tpm\n" ); + retval =0 ; + goto out;
The control flow is innovative.
Quoted 53 lines
drivers/usb/misc/xapea00x/xapea00x-core.c new file +53
-0+ +err: +dev_err (&dev->interface->dev, +"configuring SPI channel failed with %d\n" , retval); + +out: +mutex_unlock (&dev->usb_mutex); +return retval; +} + +/** +* xapea00x_spi_cleanup +* +* Context: !in_interrupt() +*/ +static void xapea00x_spi_cleanup (struct spi_device *spi) +{ +dev_dbg (&spi->dev,"%s\n" , __func__); +} + +/** +* xapea00x_spi_transfer - Execute a single SPI transfer. +* @dev: pointer to the device to do the transfer on +* @tx_buf: pointer to the data to send, if not NULL +* @rx_buf: pointer to the buffer to store the received data, if not NULL +* @len: length in bytes of the data to send/receive +* @cs_hold: If non-zero, the chip select will remain asserted +* @delay_usecs: If nonzero, how long to delay after last bit transfer +* +* Context: !in_interrupt() +* +* Return: If successful, 0. Otherwise a negative error number. +*/ +int xapea00x_spi_transfer (struct xapea00x_device *dev, +const void *tx_buf,void *rx_buf,u32 len, +int cs_hold,u16 delay_usecs) +{ +int retval; + +/* Assert chip select */ + retval =xapea00x_br_assert_cs (dev,0 ); +if (retval) + goto out; + +/* empty transfer */ +if (!tx_buf && !rx_buf) + retval =0 ; +/* read transfer */ +else if (!tx_buf) + retval =xapea00x_br_spi_read (dev, rx_buf, len); +/* write transfer */ +else if (!rx_buf) + retval =xapea00x_br_spi_write (dev, tx_buf, len);
drivers/usb/misc/xapea00x/xapea00x-core.c line 132
Quoted 10 lines
drivers/usb/misc/xapea00x/xapea00x-core.c new file +10
-0+/* write_read transfer */ + else + retval =xapea00x_br_spi_write_read (dev, tx_buf, rx_buf, len); + +/* Deassert chip select, if requested */ +if (!cs_hold) + retval =xapea00x_br_deassert_cs (dev,0 ); + +/* Delay for the requested time */ +udelay (delay_usecs);
Ouch. Do we really need to unconditionally delay? How about saving the time and using udelay only when required?
Quoted 81 lines
drivers/usb/misc/xapea00x/xapea00x-core.c new file +81
-0+ +out: +return retval; +} + +/** +* xapea00x_spi_transfer_one_message - Execute a full SPI message. +* @master: The SPI master on which to execute the message. +* @msg: The SPI message to execute. +* +* Context: !in_interrupt() +* +* Return: If successful, 0. Otherwise a negative erorr number. +*/ +static int xapea00x_spi_transfer_one_message (struct spi_master *master, +struct spi_message *msg) +{ +struct xapea00x_device *dev; +struct spi_transfer *xfer; +int is_last, retval; + + dev =spi_master_get_devdata (master); + +mutex_lock (&dev->usb_mutex); +if (!dev->interface) { + retval = -ENODEV ; + goto out; + } + +/* perform all transfers */ +list_for_each_entry (xfer, &msg->transfers, transfer_list) { + is_last =list_is_last (&xfer->transfer_list, &msg->transfers); + +/* Transfer message */ + retval =xapea00x_spi_transfer (dev, xfer->tx_buf, + xfer->rx_buf, xfer->len, + is_last == xfer->cs_change, + xfer->delay_usecs); +if (retval) + goto out; + + msg->actual_length += xfer->len; + } + + retval =0 ; + +out: + msg->status = retval; +spi_finalize_current_message (master); + +mutex_unlock (&dev->usb_mutex); +return retval; +} + +/** +* xapea00x_spi_probe - Register and configure the SPI master. +* @dev: the device whose SPI master to register +* +* Return: If successful, 0. Otherwise a negative error number. +*/ +static int xapea00x_spi_probe (struct xapea00x_device *dev) +{ +struct spi_master *spi_master; +int retval; + + spi_master =spi_alloc_master (&dev->interface->dev,sizeof (void *)); +if (!spi_master) { + retval = -ENOMEM ; + goto err_out; + } + +spi_master_set_devdata (spi_master, dev); + + spi_master->min_speed_hz =93 *1000 +800 ;/* 93.9kHz */ + spi_master->max_speed_hz =12 *1000 *1000 ;/* 12 MHz */ + + spi_master->bus_num =-1 ;/* dynamically assigned */ + spi_master->num_chipselect =1 ; + spi_master->mode_bits =SPI_MODE_0 ; + + spi_master->flags =0 ;
drivers/usb/misc/xapea00x/xapea00x-core.c line 223
Quoted 10 lines
drivers/usb/misc/xapea00x/xapea00x-core.c new file +10
-0+ spi_master->setup = xapea00x_spi_setup; + spi_master->cleanup = xapea00x_spi_cleanup; + spi_master->transfer_one_message = xapea00x_spi_transfer_one_message; + + retval =spi_register_master (spi_master); + +if (retval) + goto free_spi; + + dev->spi_master = spi_master;
Looks like a race condition
Quoted 72 lines
drivers/usb/misc/xapea00x/xapea00x-core.c new file +72
-0+dev_dbg (&dev->interface->dev,"registered SPI master\n" ); + +return 0 ; + +free_spi: +spi_master_put (spi_master); + dev->spi_master =NULL ; + +err_out: +return retval; +} + +struct xapea00x_async_probe { +struct work_struct work; +struct xapea00x_device *dev; +}; + +#define work_to_probe (w) container_of(w, struct xapea00x_async_probe, work) + +/** +* xapea00x_init_async_probe - initialize an async probe with the +* specified values. +* @probe: pointer to the async_probe to initialize +* @dev: pointer to the device to probe +* @f: pointer to the probe function +*/ +static void xapea00x_init_async_probe (struct xapea00x_async_probe *probe, +struct xapea00x_device *dev, +void (*f)(struct work_struct *work)) +{ +INIT_WORK (&probe->work, f); + probe->dev = dev; + +kref_get (&dev->kref); +spi_master_get (dev->spi_master); +} + +/** +* xapea00x_free_async_probe - clean up the internals of the async +* probe. Call this method after the probe has completed. +* +* The caller is responsible for freeing the probe itself, if +* dynamically allocated. +* +* @probe: pointer to the async_probe to clean up +*/ +static void xapea00x_cleanup_async_probe (struct xapea00x_async_probe *probe) +{ +spi_master_put (probe->dev->spi_master); +kref_put (&probe->dev->kref, xapea00x_delete); +} + +static struct spi_board_info tpm_board_info = { + .modalias =XAPEA00X_TPM_MODALIAS , + .max_speed_hz =43 *1000 *1000 ,// Hz + .chip_select =0 , + .mode =SPI_MODE_0 , + .platform_data =NULL , + .controller_data =NULL , +}; + +/** +* xapea00x_tpm_probe - Register and initialize the TPM device +* @work: the work struct contained by the xapea00x device +* +* Context: !in_interrupt() +*/ +static void xapea00x_tpm_probe (struct work_struct *work) +{ +struct xapea00x_async_probe *probe =work_to_probe (work); +struct xapea00x_device *dev = probe->dev; +struct spi_master *spi_master = dev->spi_master;
drivers/usb/misc/xapea00x/xapea00x-core.c line 305
Quoted 10 lines
drivers/usb/misc/xapea00x/xapea00x-core.c new file +10
-0+struct spi_device *tpm; +int retval; + + tpm =spi_new_device (spi_master, &tpm_board_info); +mutex_lock (&dev->usb_mutex); +if (!dev->interface) { + retval = -ENODEV ; + goto out; + } +if (!tpm) {
Why test this under a mutex?
Quoted 65 lines
drivers/usb/misc/xapea00x/xapea00x-core.c new file +65
-0+ retval = -ENODEV ; +dev_err (&dev->interface->dev, +"unable to add spi device for TPM\n" ); + goto err; + } + + dev->tpm = tpm; +dev_info (&dev->interface->dev,"TPM initialization complete\n" ); + + retval =0 ; + goto out; + +err: +dev_err (&dev->interface->dev, +"TPM initialization failed with %d\n" , retval); + +out: +mutex_unlock (&dev->usb_mutex); +xapea00x_cleanup_async_probe (probe); +kzfree (probe); +} + +/******************************************************************************* +* USB driver structs and functions +*/ + +static const struct usb_device_id xapea00x_devices[] = { + {USB_DEVICE (USB_VENDOR_ID_SILABS ,USB_PRODUCT_ID_XAPEA001 ) }, + {USB_DEVICE (USB_VENDOR_ID_XAPTUM ,USB_PRODUCT_ID_XAPEA002 ) }, + {USB_DEVICE (USB_VENDOR_ID_XAPTUM ,USB_PRODUCT_ID_XAPEA003 ) }, + { } +}; +MODULE_DEVICE_TABLE (usb, xapea00x_devices); + +static int xapea00x_probe (struct usb_interface *interface, +const struct usb_device_id *id) +{ +struct xapea00x_device *dev; +struct xapea00x_async_probe *probe; +int retval; + + dev =kzalloc (sizeof (*dev),GFP_KERNEL ); +if (!dev) +return -ENOMEM ; + +kref_init (&dev->kref); +mutex_init (&dev->usb_mutex); + +/* ---------------------- USB ------------------------ */ + dev->interface = interface; + dev->udev =usb_get_dev (interface_to_usbdev (interface)); + + dev->vid =__le16_to_cpu (dev->udev->descriptor.idVendor); + dev->pid =__le16_to_cpu (dev->udev->descriptor.idProduct); + + retval =usb_find_common_endpoints (interface->cur_altsetting, + &dev->bulk_in, &dev->bulk_out, +NULL ,NULL ); +if (retval) { +dev_err (&interface->dev, +"could not find both bulk-in and bulk-out endpoints\n" ); + goto err_out; + } + +usb_set_intfdata (interface, dev);
drivers/usb/misc/xapea00x/xapea00x-core.c line 380
Quoted 10 lines
drivers/usb/misc/xapea00x/xapea00x-core.c new file +10
-0+ +/* ---------------------- SPI Master ------------------------ */ + retval =xapea00x_spi_probe (dev); +if (retval) { +dev_err (&interface->dev,"could not initialize SPI master\n" ); + goto err_out; + } + +/* ---------------------- TPM SPI Device ------------------------ */ + probe =kzalloc (sizeof (*probe),GFP_KERNEL );
Oops in the failure case
Quoted 335 lines
drivers/usb/misc/xapea00x/xapea00x-core.c new file +49
-0+xapea00x_init_async_probe (probe, dev, xapea00x_tpm_probe); + +schedule_work (&probe->work); +dev_info (&interface->dev,"scheduled initialization of TPM\n" ); + +/* ---------------------- Finished ------------------------ */ +dev_info (&interface->dev,"device connected\n" ); +return 0 ; + +err_out: +dev_err (&interface->dev,"device failed with %d\n" , retval); +kref_put (&dev->kref, xapea00x_delete); +return retval; +} + +static void xapea00x_disconnect (struct usb_interface *interface) +{ +struct xapea00x_device *dev =usb_get_intfdata (interface); + +usb_set_intfdata (interface,NULL ); +spi_unregister_master (dev->spi_master); + +mutex_lock (&dev->usb_mutex); + dev->interface =NULL ; +mutex_unlock (&dev->usb_mutex); + +kref_put (&dev->kref, xapea00x_delete); + +dev_info (&dev->udev->dev,"device disconnected\n" ); +} + +static struct usb_driver xapea00x_driver = { + .name ="xapea00x" , + .probe = xapea00x_probe, + .disconnect = xapea00x_disconnect, + .suspend =NULL , + .resume =NULL , + .reset_resume =NULL , + .id_table = xapea00x_devices, + .supports_autosuspend =0 +}; + +module_usb_driver (xapea00x_driver); + +MODULE_AUTHOR ("David R. Bild <[email protected]>" ); +MODULE_DESCRIPTION ("Xaptum XAP-EA-00x ENF Access card" ); +MODULE_LICENSE ("GPL" ); + +MODULE_ALIAS ("xapea00x" );drivers/usb/misc/xapea00x/xapea00x-spi.c new file +209
-0@@ -0,0 +1,209 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* +* Driver for the XAP-EA-00x series of the Xaptum Edge Access Card, a +* TPM 2.0-based hardware module for authenticating IoT devices and +* gateways. +* +* Copyright (c) 2017 Xaptum, Inc. +*/ + +#include "xapea00x.h" + +/******************************************************************************* +* SPI master functions +*/ + +/** +* xapea00x_spi_setup - Setup the SPI channel for the TPM. +* +* Context: !in_interrupt() +* +* Return: If successful, 0. Otherwise a negative error number. +*/ +static int xapea00x_spi_setup (struct spi_device *spi) +{ +struct xapea00x_device *dev; +int retval; + + dev =spi_master_get_devdata (spi->master); + +/* Verify that this is the TPM device */ +if (spi->chip_select !=0 ) { + retval = -EINVAL ; + goto err; + } + +/* Set the SPI parameters for the TPM channel. */ + retval =xapea00x_br_set_spi_word (dev,0 ,XAPEA00X_TPM_SPI_WORD ); +if (retval) + goto err; + +/* +* Disable auto chip select for the TPM channel. +* Must be done after setting the SPI parameters. +*/ + retval =xapea00x_br_set_gpio_cs (dev,0 ,XAPEA00X_GPIO_CS_DISABLED ); +if (retval) + goto err; + +/* De-assert chip select for the TPM channel. */ + retval =xapea00x_br_set_gpio_value (dev,0 ,1 ); +if (retval) + goto err; + +dev_dbg (&dev->interface->dev,"configured spi channel for tpm\n" ); +return 0 ; + +err: +dev_err (&dev->interface->dev, +"configuring SPI channel failed with %d\n" , retval); +return retval; +} + +/** +* xapea00x_spi_cleanup +* +* Context: !in_interrupt() +*/ +static void xapea00x_spi_cleanup (struct spi_device *spi) +{ +dev_dbg (&spi->dev,"%s\n" , __func__); +} + +/** +* xapea00x_spi_transfer - Execute a single SPI transfer. +* @dev: pointer to the device to do the transfer on +* @tx_buf: pointer to the data to send, if not NULL +* @rx_buf: pointer to the buffer to store the received data, if not NULL +* @len: length in bytes of the data to send/receive +* @cs_hold: If non-zero, the chip select will remain asserted +* @delay_usecs: If nonzero, how long to delay after last bit transfer +* +* Context: !in_interrupt() +* +* Return: If successful, 0. Otherwise a negative error number. +*/ +int xapea00x_spi_transfer (struct xapea00x_device *dev, +const void *tx_buf,void *rx_buf,u32 len, +int cs_hold,u16 delay_usecs) +{ +int retval; + +/* Assert chip select */ + retval =xapea00x_br_set_gpio_value (dev,0 ,0 ); +if (retval) + goto out; + +/* empty transfer */ +if (!tx_buf && !rx_buf) + retval =0 ; +/* read transfer */ +else if (!tx_buf) + retval =xapea00x_br_spi_read (dev, rx_buf, len); +/* write transfer */ +else if (!rx_buf) + retval =xapea00x_br_spi_write (dev, tx_buf, len); +/* write_read transfer */ +else + retval =xapea00x_br_spi_write_read (dev, tx_buf, rx_buf, len); + +/* Deassert chip select, if requested */ +if (!cs_hold) + retval =xapea00x_br_set_gpio_value (dev,0 ,1 ); + +/* Delay for the requested time */ +udelay (delay_usecs); + +out: +return retval; +} + +/** +* xapea00x_spi_transfer_one_message - Execute a full SPI message. +* @master: The SPI master on which to execute the message. +* @msg: The SPI message to execute. +* +* Context: !in_interrupt() +* +* Return: If successful, 0. Otherwise a negative erorr number. +*/ +static int xapea00x_spi_transfer_one_message (struct spi_master *master, +struct spi_message *msg) +{ +struct xapea00x_device *dev; +struct spi_transfer *xfer; +int is_last, retval; + + dev =spi_master_get_devdata (master); + +/* perform all transfers */ +list_for_each_entry (xfer, &msg->transfers, transfer_list) { + is_last =list_is_last (&xfer->transfer_list, &msg->transfers); + +/* Transfer message */ + retval =xapea00x_spi_transfer (dev, xfer->tx_buf, + xfer->rx_buf, xfer->len, + is_last == xfer->cs_change, + xfer->delay_usecs); +if (retval) + goto out; + + msg->actual_length += xfer->len; + } + + retval =0 ; + +out: + msg->status = retval; +spi_finalize_current_message (master); +return retval; +} + +/** +* xapea00x_spi_probe - Register and configure the SPI master. +* @dev: the device whose SPI master to register +* +* Return: If successful, 0. Otherwise a negative error number. +*/ +static int xapea00x_spi_probe (struct xapea00x_device *dev) +{ +struct spi_master *spi_master; +int retval; + + spi_master =spi_alloc_master (&dev->udev->dev,sizeof (void *)); +if (!spi_master) { + retval = -ENOMEM ; + goto err_out; + } + +spi_master_set_devdata (spi_master, dev); + + spi_master->min_speed_hz =93 *1000 +800 ;/* 93.9kHz */ + spi_master->max_speed_hz =12 *1000 *1000 ;/* 12 MHz */ + + spi_master->bus_num =-1 ;/* dynamically assigned */ + spi_master->num_chipselect =XAPEA00X_NUM_CS ; + spi_master->mode_bits =SPI_MODE_0 ; + + spi_master->flags =0 ; + spi_master->setup = xapea00x_spi_setup; + spi_master->cleanup = xapea00x_spi_cleanup; + spi_master->transfer_one_message = xapea00x_spi_transfer_one_message; + + retval =spi_register_master (spi_master); + +if (retval) + goto free_spi; + + dev->spi_master = spi_master; +dev_dbg (&dev->interface->dev,"registered SPI master\n" ); + +return 0 ; + +free_spi: +spi_master_put (spi_master); + dev->spi_master =NULL ; + +err_out: +return retval; +}drivers/usb/misc/xapea00x/xapea00x.h new file +65
-0@@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* +* Driver for the XAP-EA-00x series of the Xaptum Edge Access Card, a +* TPM 2.0-based hardware module for authenticating IoT devices and +* gateways. +* +* Copyright (c) 2017 Xaptum, Inc. +*/ + +#ifndef _XAPEA00X_H +#define _XAPEA00X_H + +#include <linux/kernel.h> +#include <linux/kref.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> +#include <linux/usb.h> +#include <linux/workqueue.h> + +#define USB_VENDOR_ID_SILABS 0x10c4 +#define USB_VENDOR_ID_XAPTUM 0x2FE0 + +#define USB_PRODUCT_ID_XAPEA001 0x8BDE +#define USB_PRODUCT_ID_XAPEA002 0x8BDE +#define USB_PRODUCT_ID_XAPEA003 0x8BEE + +struct xapea00x_device { +struct kref kref; + +struct usb_device *udev; +/* +* The interface pointer will be set NULL when the device +* disconnects. Accessing it safe only while holding the +* usb_mutex. +*/ +struct usb_interface *interface; +/* +* Th usb_mutex must be held while synchronous USB requests are +* in progress. It is acquired during disconnect to be sure +* that there is not an outstanding request. +*/ +struct mutex usb_mutex; + +struct usb_endpoint_descriptor *bulk_in; +struct usb_endpoint_descriptor *bulk_out; + +u16 pid; +u16 vid; + +struct spi_master *spi_master; +struct spi_device *tpm; +}; + +/* Public bridge functions */ +int xapea00x_br_disable_cs (struct xapea00x_device *dev,u8 channel); +int xapea00x_br_assert_cs (struct xapea00x_device *dev,u8 channel); +int xapea00x_br_deassert_cs (struct xapea00x_device *dev,u8 channel); + +int xapea00x_br_spi_read (struct xapea00x_device *dev,void *rx_buf,int len); +int xapea00x_br_spi_write (struct xapea00x_device *dev,const void *tx_buf, +int len); +int xapea00x_br_spi_write_read (struct xapea00x_device *dev,const void *tx_buf, +void *rx_buf,int len);
drivers/usb/misc/xapea00x/xapea00x.h line 75
Quoted 10 lines
drivers/usb/misc/xapea00x/xapea00x.h new file +10
-0+ +/* Shared SPI function */ +int xapea00x_spi_transfer (struct xapea00x_device *dev, +const void *tx_buf,void *rx_buf,u32 len, +int cs_hold,u16 delay_usecs); + +/* Shared TPM functions */ +int xapea00x_tpm_platform_initialize (struct xapea00x_device *dev); + +#endif /* _XAPEA00X_H */
---
To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html