Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/bluetooth/iwmbtfw/iwmbt_hw.c
Show First 20 Lines • Show All 128 Lines • ▼ Show 20 Lines | |||||
int | int | ||||
iwmbt_patch_fwfile(struct libusb_device_handle *hdl, | iwmbt_patch_fwfile(struct libusb_device_handle *hdl, | ||||
const struct iwmbt_firmware *fw) | const struct iwmbt_firmware *fw) | ||||
{ | { | ||||
int ret, transferred; | int ret, transferred; | ||||
struct iwmbt_firmware fw_job = *fw; | struct iwmbt_firmware fw_job = *fw; | ||||
uint16_t cmd_opcode; | uint16_t cmd_opcode; | ||||
uint8_t cmd_length; | uint8_t cmd_length; | ||||
uint8_t cmd_buf[IWMBT_HCI_MAX_CMD_SIZE]; | struct iwmbt_hci_cmd *cmd_buf; | ||||
uint8_t evt_code; | uint8_t evt_code; | ||||
uint8_t evt_length; | uint8_t evt_length; | ||||
uint8_t evt_buf[IWMBT_HCI_MAX_EVENT_SIZE]; | uint8_t evt_buf[IWMBT_HCI_MAX_EVENT_SIZE]; | ||||
int skip_patch = 0; | int activate_patch = 0; | ||||
for (;;) { | while (fw_job.len > 0) { | ||||
skip_patch = 0; | if (fw_job.len < 4) { | ||||
iwmbt_err("Invalid firmware, unexpected EOF in HCI " | |||||
"command header. Remains=%d", fw_job.len); | |||||
return (-1); | |||||
} | |||||
if (fw_job.len < 4) | |||||
break; | |||||
if (fw_job.buf[0] != 0x01) { | if (fw_job.buf[0] != 0x01) { | ||||
iwmbt_err("Invalid firmware, expected HCI command (%d)", | iwmbt_err("Invalid firmware, expected HCI command (%d)", | ||||
fw_job.buf[0]); | fw_job.buf[0]); | ||||
return (-1); | return (-1); | ||||
} | } | ||||
/* Advance by one. */ | /* Advance by one. */ | ||||
fw_job.buf++; | fw_job.buf++; | ||||
fw_job.len--; | fw_job.len--; | ||||
/* Load in the HCI command to perform. */ | /* Load in the HCI command to perform. */ | ||||
cmd_opcode = le16dec(fw_job.buf); | cmd_opcode = le16dec(fw_job.buf); | ||||
cmd_length = fw_job.buf[2]; | cmd_length = fw_job.buf[2]; | ||||
memcpy(cmd_buf, fw_job.buf, 3); | cmd_buf = (struct iwmbt_hci_cmd *)fw_job.buf; | ||||
iwmbt_debug("opcode=%04x, len=%02x", cmd_opcode, cmd_length); | iwmbt_debug("opcode=%04x, len=%02x", cmd_opcode, cmd_length); | ||||
/* For some reason the command 0xfc2f hangs up my card. */ | /* | ||||
if (cmd_opcode == 0xfc2f) | * If there is a command that loads a patch in the | ||||
skip_patch = 1; | * firmware file, then activate the patch upon success, | ||||
* otherwise just disable the manufacturer mode. | |||||
*/ | |||||
if (cmd_opcode == 0xfc8e) | |||||
activate_patch = 1; | |||||
/* Advance by three. */ | /* Advance by three. */ | ||||
fw_job.buf += 3; | fw_job.buf += 3; | ||||
fw_job.len -= 3; | fw_job.len -= 3; | ||||
if (fw_job.len < cmd_length) | if (fw_job.len < cmd_length) { | ||||
cmd_length = fw_job.len; | iwmbt_err("Invalid firmware, unexpected EOF in HCI " | ||||
"command data. len=%d, remains=%d", | |||||
cmd_length, fw_job.len); | |||||
return (-1); | |||||
} | |||||
/* Copy data to HCI command buffer. */ | |||||
memcpy(cmd_buf + 3, fw_job.buf, | |||||
MIN(cmd_length, IWMBT_HCI_MAX_CMD_SIZE - 3)); | |||||
/* Advance by data length. */ | /* Advance by data length. */ | ||||
fw_job.buf += cmd_length; | fw_job.buf += cmd_length; | ||||
fw_job.len -= cmd_length; | fw_job.len -= cmd_length; | ||||
ret = libusb_control_transfer(hdl, | |||||
LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_DEVICE, | |||||
0, | |||||
0, | |||||
0, | |||||
(uint8_t *)cmd_buf, | |||||
IWMBT_HCI_CMD_SIZE(cmd_buf), | |||||
IWMBT_HCI_CMD_TIMEOUT); | |||||
if (ret < 0) { | |||||
iwmbt_err("libusb_control_transfer() failed: err=%s", | |||||
libusb_strerror(ret)); | |||||
return (-1); | |||||
} | |||||
/* | /* | ||||
* Every command has its associated event: data must match | * Every command has its associated event: data must match | ||||
* what is recorded in the firmware file. Perform that check | * what is recorded in the firmware file. Perform that check | ||||
* now. | * now. | ||||
* | |||||
* Some commands are mapped to more than one event sequence, | |||||
* in that case we can drop the non-patch commands, as we | |||||
* probably don't need them for operation of the card. | |||||
* | |||||
*/ | */ | ||||
for (;;) { | while (fw_job.len > 0 && fw_job.buf[0] == 0x02) { | ||||
/* Is this the end of the file? */ | /* Is this the end of the file? */ | ||||
if (fw_job.len < 3) | if (fw_job.len < 3) { | ||||
break; | iwmbt_err("Invalid firmware, unexpected EOF in" | ||||
"event header. remains=%d", fw_job.len); | |||||
return (-1); | |||||
} | |||||
if (fw_job.buf[0] != 0x02) | |||||
break; | |||||
/* Advance by one. */ | /* Advance by one. */ | ||||
fw_job.buf++; | fw_job.buf++; | ||||
fw_job.len--; | fw_job.len--; | ||||
/* Load in the HCI event. */ | /* Load in the HCI event. */ | ||||
evt_code = fw_job.buf[0]; | evt_code = fw_job.buf[0]; | ||||
evt_length = fw_job.buf[1]; | evt_length = fw_job.buf[1]; | ||||
/* Advance by two. */ | /* Advance by two. */ | ||||
fw_job.buf += 2; | fw_job.buf += 2; | ||||
fw_job.len -= 2; | fw_job.len -= 2; | ||||
/* Prepare HCI event buffer. */ | /* Prepare HCI event buffer. */ | ||||
memset(evt_buf, 0, IWMBT_HCI_MAX_EVENT_SIZE); | memset(evt_buf, 0, IWMBT_HCI_MAX_EVENT_SIZE); | ||||
iwmbt_debug("event=%04x, len=%02x", | iwmbt_debug("event=%04x, len=%02x", | ||||
evt_code, evt_length); | evt_code, evt_length); | ||||
/* Advance by data length. */ | if (fw_job.len < evt_length) { | ||||
fw_job.buf += evt_length; | iwmbt_err("Invalid firmware, unexpected EOF in" | ||||
fw_job.len -= evt_length; | " event data. len=%d, remains=%d", | ||||
evt_length, fw_job.len); | |||||
return (-1); | |||||
} | |||||
if (skip_patch == 0) { | ret = libusb_interrupt_transfer(hdl, | ||||
ret = iwmbt_hci_command(hdl, | IWMBT_INTERRUPT_ENDPOINT_ADDR, | ||||
(struct iwmbt_hci_cmd *)cmd_buf, | |||||
evt_buf, | evt_buf, | ||||
IWMBT_HCI_MAX_EVENT_SIZE, | IWMBT_HCI_MAX_EVENT_SIZE, | ||||
&transferred, | &transferred, | ||||
IWMBT_HCI_CMD_TIMEOUT); | IWMBT_HCI_CMD_TIMEOUT); | ||||
if (ret < 0) { | if (ret < 0) { | ||||
iwmbt_debug("Can't send patch: " | iwmbt_err("libusb_interrupt_transfer() failed:" | ||||
"code=%d, size=%d", | " err=%s", libusb_strerror(ret)); | ||||
ret, | |||||
transferred); | |||||
return (-1); | return (-1); | ||||
} | } | ||||
if ((int)evt_length + 2 != transferred || | |||||
memcmp(evt_buf + 2, fw_job.buf, evt_length) != 0) { | |||||
iwmbt_err("event does not match firmware"); | |||||
return (-1); | |||||
} | } | ||||
/* Advance by data length. */ | |||||
fw_job.buf += evt_length; | |||||
fw_job.len -= evt_length; | |||||
} | } | ||||
} | } | ||||
return (0); | return (activate_patch); | ||||
} | } | ||||
int | int | ||||
iwmbt_load_fwfile(struct libusb_device_handle *hdl, | iwmbt_load_fwfile(struct libusb_device_handle *hdl, | ||||
const struct iwmbt_firmware *fw, uint32_t *boot_param) | const struct iwmbt_firmware *fw, uint32_t *boot_param) | ||||
{ | { | ||||
int ready = 0, sent = 0; | int ready = 0, sent = 0; | ||||
int ret, transferred; | int ret, transferred; | ||||
▲ Show 20 Lines • Show All 326 Lines • Show Last 20 Lines |