Bluetooth: btintel: Add combined setup and shutdown functions
There are multiple setup and shutdown functions for Intel device and the setup function to be used is depends on the USB PID/VID, which makes difficult to maintain the code and increases the code size. This patch adds combined setup and shutdown functions to provide a single entry point for all Intel devices and choose the setup functions based on the information read with HCI_Intel_Read_Version command. Starting from TyP device, the command and response parameters for HCI_Intel_Read_Version command are changed even though OCF remains same. However, the legacy devices still can handle the command without error even if it has a extra parameter, so to simplify the flow, the new command format is used to read the version information for both legacy and new (tlv based) format. Also, it also adds a routine to setup the hdev callbacks in btintel. Signed-off-by: Tedd Ho-Jeong An <tedd.an@intel.com> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
This commit is contained in:
parent
6ec566131d
commit
ca5425e158
|
@ -236,6 +236,8 @@ int btintel_version_info(struct hci_dev *hdev, struct intel_version *ver)
|
||||||
* compatibility options when newer hardware variants come along.
|
* compatibility options when newer hardware variants come along.
|
||||||
*/
|
*/
|
||||||
switch (ver->hw_variant) {
|
switch (ver->hw_variant) {
|
||||||
|
case 0x07: /* WP - Legacy ROM */
|
||||||
|
case 0x08: /* StP - Legacy ROM */
|
||||||
case 0x0b: /* SfP */
|
case 0x0b: /* SfP */
|
||||||
case 0x0c: /* WsP */
|
case 0x0c: /* WsP */
|
||||||
case 0x11: /* JfP */
|
case 0x11: /* JfP */
|
||||||
|
@ -250,9 +252,15 @@ int btintel_version_info(struct hci_dev *hdev, struct intel_version *ver)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (ver->fw_variant) {
|
switch (ver->fw_variant) {
|
||||||
|
case 0x01:
|
||||||
|
variant = "Legacy ROM 2.5";
|
||||||
|
break;
|
||||||
case 0x06:
|
case 0x06:
|
||||||
variant = "Bootloader";
|
variant = "Bootloader";
|
||||||
break;
|
break;
|
||||||
|
case 0x22:
|
||||||
|
variant = "Legacy ROM 2.x";
|
||||||
|
break;
|
||||||
case 0x23:
|
case 0x23:
|
||||||
variant = "Firmware";
|
variant = "Firmware";
|
||||||
break;
|
break;
|
||||||
|
@ -483,6 +491,108 @@ int btintel_version_info_tlv(struct hci_dev *hdev, struct intel_version_tlv *ver
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(btintel_version_info_tlv);
|
EXPORT_SYMBOL_GPL(btintel_version_info_tlv);
|
||||||
|
|
||||||
|
static int btintel_parse_version_tlv(struct hci_dev *hdev,
|
||||||
|
struct intel_version_tlv *version,
|
||||||
|
struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
/* Consume Command Complete Status field */
|
||||||
|
skb_pull(skb, 1);
|
||||||
|
|
||||||
|
/* Event parameters contatin multiple TLVs. Read each of them
|
||||||
|
* and only keep the required data. Also, it use existing legacy
|
||||||
|
* version field like hw_platform, hw_variant, and fw_variant
|
||||||
|
* to keep the existing setup flow
|
||||||
|
*/
|
||||||
|
while (skb->len) {
|
||||||
|
struct intel_tlv *tlv;
|
||||||
|
|
||||||
|
/* Make sure skb has a minimum length of the header */
|
||||||
|
if (skb->len < sizeof(*tlv))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
tlv = (struct intel_tlv *)skb->data;
|
||||||
|
|
||||||
|
/* Make sure skb has a enough data */
|
||||||
|
if (skb->len < tlv->len + sizeof(*tlv))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
switch (tlv->type) {
|
||||||
|
case INTEL_TLV_CNVI_TOP:
|
||||||
|
version->cnvi_top = get_unaligned_le32(tlv->val);
|
||||||
|
break;
|
||||||
|
case INTEL_TLV_CNVR_TOP:
|
||||||
|
version->cnvr_top = get_unaligned_le32(tlv->val);
|
||||||
|
break;
|
||||||
|
case INTEL_TLV_CNVI_BT:
|
||||||
|
version->cnvi_bt = get_unaligned_le32(tlv->val);
|
||||||
|
break;
|
||||||
|
case INTEL_TLV_CNVR_BT:
|
||||||
|
version->cnvr_bt = get_unaligned_le32(tlv->val);
|
||||||
|
break;
|
||||||
|
case INTEL_TLV_DEV_REV_ID:
|
||||||
|
version->dev_rev_id = get_unaligned_le16(tlv->val);
|
||||||
|
break;
|
||||||
|
case INTEL_TLV_IMAGE_TYPE:
|
||||||
|
version->img_type = tlv->val[0];
|
||||||
|
break;
|
||||||
|
case INTEL_TLV_TIME_STAMP:
|
||||||
|
/* If image type is Operational firmware (0x03), then
|
||||||
|
* running FW Calendar Week and Year information can
|
||||||
|
* be extracted from Timestamp information
|
||||||
|
*/
|
||||||
|
version->min_fw_build_cw = tlv->val[0];
|
||||||
|
version->min_fw_build_yy = tlv->val[1];
|
||||||
|
version->timestamp = get_unaligned_le16(tlv->val);
|
||||||
|
break;
|
||||||
|
case INTEL_TLV_BUILD_TYPE:
|
||||||
|
version->build_type = tlv->val[0];
|
||||||
|
break;
|
||||||
|
case INTEL_TLV_BUILD_NUM:
|
||||||
|
/* If image type is Operational firmware (0x03), then
|
||||||
|
* running FW build number can be extracted from the
|
||||||
|
* Build information
|
||||||
|
*/
|
||||||
|
version->min_fw_build_nn = tlv->val[0];
|
||||||
|
version->build_num = get_unaligned_le32(tlv->val);
|
||||||
|
break;
|
||||||
|
case INTEL_TLV_SECURE_BOOT:
|
||||||
|
version->secure_boot = tlv->val[0];
|
||||||
|
break;
|
||||||
|
case INTEL_TLV_OTP_LOCK:
|
||||||
|
version->otp_lock = tlv->val[0];
|
||||||
|
break;
|
||||||
|
case INTEL_TLV_API_LOCK:
|
||||||
|
version->api_lock = tlv->val[0];
|
||||||
|
break;
|
||||||
|
case INTEL_TLV_DEBUG_LOCK:
|
||||||
|
version->debug_lock = tlv->val[0];
|
||||||
|
break;
|
||||||
|
case INTEL_TLV_MIN_FW:
|
||||||
|
version->min_fw_build_nn = tlv->val[0];
|
||||||
|
version->min_fw_build_cw = tlv->val[1];
|
||||||
|
version->min_fw_build_yy = tlv->val[2];
|
||||||
|
break;
|
||||||
|
case INTEL_TLV_LIMITED_CCE:
|
||||||
|
version->limited_cce = tlv->val[0];
|
||||||
|
break;
|
||||||
|
case INTEL_TLV_SBE_TYPE:
|
||||||
|
version->sbe_type = tlv->val[0];
|
||||||
|
break;
|
||||||
|
case INTEL_TLV_OTP_BDADDR:
|
||||||
|
memcpy(&version->otp_bd_addr, tlv->val,
|
||||||
|
sizeof(bdaddr_t));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* Ignore rest of information */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* consume the current tlv and move to next*/
|
||||||
|
skb_pull(skb, tlv->len + sizeof(*tlv));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int btintel_read_version_tlv(struct hci_dev *hdev, struct intel_version_tlv *version)
|
int btintel_read_version_tlv(struct hci_dev *hdev, struct intel_version_tlv *version)
|
||||||
{
|
{
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
|
@ -1272,6 +1382,131 @@ int btintel_set_debug_features(struct hci_dev *hdev,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(btintel_set_debug_features);
|
EXPORT_SYMBOL_GPL(btintel_set_debug_features);
|
||||||
|
|
||||||
|
static int btintel_setup_combined(struct hci_dev *hdev)
|
||||||
|
{
|
||||||
|
const u8 param[1] = { 0xFF };
|
||||||
|
struct intel_version ver;
|
||||||
|
struct intel_version_tlv ver_tlv;
|
||||||
|
struct sk_buff *skb;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
BT_DBG("%s", hdev->name);
|
||||||
|
|
||||||
|
/* Starting from TyP device, the command parameter and response are
|
||||||
|
* changed even though the OCF for HCI_Intel_Read_Version command
|
||||||
|
* remains same. The legacy devices can handle even if the
|
||||||
|
* command has a parameter and returns a correct version information.
|
||||||
|
* So, it uses new format to support both legacy and new format.
|
||||||
|
*/
|
||||||
|
skb = __hci_cmd_sync(hdev, 0xfc05, 1, param, HCI_CMD_TIMEOUT);
|
||||||
|
if (IS_ERR(skb)) {
|
||||||
|
bt_dev_err(hdev, "Reading Intel version command failed (%ld)",
|
||||||
|
PTR_ERR(skb));
|
||||||
|
return PTR_ERR(skb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check the status */
|
||||||
|
if (skb->data[0]) {
|
||||||
|
bt_dev_err(hdev, "Intel Read Version command failed (%02x)",
|
||||||
|
skb->data[0]);
|
||||||
|
err = -EIO;
|
||||||
|
goto exit_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For Legacy device, check the HW platform value and size */
|
||||||
|
if (skb->len == sizeof(ver) && skb->data[1] == 0x37) {
|
||||||
|
bt_dev_dbg(hdev, "Read the legacy Intel version information");
|
||||||
|
|
||||||
|
memcpy(&ver, skb->data, sizeof(ver));
|
||||||
|
|
||||||
|
/* Display version information */
|
||||||
|
btintel_version_info(hdev, &ver);
|
||||||
|
|
||||||
|
/* Check for supported iBT hardware variants of this firmware
|
||||||
|
* loading method.
|
||||||
|
*
|
||||||
|
* This check has been put in place to ensure correct forward
|
||||||
|
* compatibility options when newer hardware variants come
|
||||||
|
* along.
|
||||||
|
*/
|
||||||
|
switch (ver.hw_variant) {
|
||||||
|
case 0x07: /* WP */
|
||||||
|
case 0x08: /* StP */
|
||||||
|
/* Legacy ROM product */
|
||||||
|
/* TODO: call setup routine for legacy rom product */
|
||||||
|
break;
|
||||||
|
case 0x0b: /* SfP */
|
||||||
|
case 0x0c: /* WsP */
|
||||||
|
case 0x11: /* JfP */
|
||||||
|
case 0x12: /* ThP */
|
||||||
|
case 0x13: /* HrP */
|
||||||
|
case 0x14: /* CcP */
|
||||||
|
/* TODO: call setup routine for bootloader product */
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
bt_dev_err(hdev, "Unsupported Intel hw variant (%u)",
|
||||||
|
ver.hw_variant);
|
||||||
|
err = -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
goto exit_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For TLV type device, parse the tlv data */
|
||||||
|
err = btintel_parse_version_tlv(hdev, &ver_tlv, skb);
|
||||||
|
if (err) {
|
||||||
|
bt_dev_err(hdev, "Failed to parse TLV version information");
|
||||||
|
goto exit_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (INTEL_HW_PLATFORM(ver_tlv.cnvi_bt) != 0x37) {
|
||||||
|
bt_dev_err(hdev, "Unsupported Intel hardware platform (0x%2x)",
|
||||||
|
INTEL_HW_PLATFORM(ver_tlv.cnvi_bt));
|
||||||
|
err = -EINVAL;
|
||||||
|
goto exit_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Display version information of TLV type */
|
||||||
|
btintel_version_info_tlv(hdev, &ver_tlv);
|
||||||
|
|
||||||
|
/* TODO: Need to filter the device for new generation */
|
||||||
|
/* TODO: call setup routine for tlv based bootloader product */
|
||||||
|
|
||||||
|
exit_error:
|
||||||
|
kfree_skb(skb);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int btintel_shutdown_combined(struct hci_dev *hdev)
|
||||||
|
{
|
||||||
|
struct sk_buff *skb;
|
||||||
|
|
||||||
|
/* Send HCI Reset to the controller to stop any BT activity which
|
||||||
|
* were triggered. This will help to save power and maintain the
|
||||||
|
* sync b/w Host and controller
|
||||||
|
*/
|
||||||
|
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
|
||||||
|
if (IS_ERR(skb)) {
|
||||||
|
bt_dev_err(hdev, "HCI reset during shutdown failed");
|
||||||
|
return PTR_ERR(skb);
|
||||||
|
}
|
||||||
|
kfree_skb(skb);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int btintel_configure_setup(struct hci_dev *hdev)
|
||||||
|
{
|
||||||
|
/* TODO: Setup hdev callback here */
|
||||||
|
hdev->manufacturer = 2;
|
||||||
|
hdev->setup = btintel_setup_combined;
|
||||||
|
hdev->shutdown = btintel_shutdown_combined;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(btintel_configure_setup);
|
||||||
|
|
||||||
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
|
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
|
||||||
MODULE_DESCRIPTION("Bluetooth support for Intel devices ver " VERSION);
|
MODULE_DESCRIPTION("Bluetooth support for Intel devices ver " VERSION);
|
||||||
MODULE_VERSION(VERSION);
|
MODULE_VERSION(VERSION);
|
||||||
|
|
|
@ -175,6 +175,7 @@ int btintel_read_debug_features(struct hci_dev *hdev,
|
||||||
struct intel_debug_features *features);
|
struct intel_debug_features *features);
|
||||||
int btintel_set_debug_features(struct hci_dev *hdev,
|
int btintel_set_debug_features(struct hci_dev *hdev,
|
||||||
const struct intel_debug_features *features);
|
const struct intel_debug_features *features);
|
||||||
|
int btintel_configure_setup(struct hci_dev *hdev);
|
||||||
#else
|
#else
|
||||||
|
|
||||||
static inline int btintel_check_bdaddr(struct hci_dev *hdev)
|
static inline int btintel_check_bdaddr(struct hci_dev *hdev)
|
||||||
|
@ -307,4 +308,9 @@ static inline int btintel_set_debug_features(struct hci_dev *hdev,
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int btintel_configure_setup(struct hci_dev *hdev)
|
||||||
|
{
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue