| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2012 Intel Corporation. All rights reserved.
- *
- *
- */
- #ifdef HAVE_CONFIG_H
- #include <config.h>
- #endif
- #define _GNU_SOURCE
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <string.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <sys/param.h>
- #include <sys/ioctl.h>
- #include <time.h>
- #include "lib/bluetooth.h"
- #include "lib/hci.h"
- #include "lib/hci_lib.h"
- #include "hciattach.h"
- #ifdef INTEL_DEBUG
- #define DBGPRINT(fmt, args...) printf("DBG: " fmt "\n", ## args)
- #define PRINT_PACKET(buf, len, msg) { \
- int i; \
- printf("%s\n", msg); \
- for (i = 0; i < len; i++) \
- printf("%02X ", buf[i]); \
- printf("\n"); \
- }
- #else
- #define DBGPRINT(fmt, args...)
- #define PRINT_PACKET(buf, len, msg)
- #endif
- #define PATCH_SEQ_EXT ".bseq"
- #define PATCH_FILE_PATH "/lib/firmware/intel/"
- #define PATCH_MAX_LEN 260
- #define PATCH_TYPE_CMD 1
- #define PATCH_TYPE_EVT 2
- #define INTEL_VER_PARAM_LEN 9
- #define INTEL_MFG_PARAM_LEN 2
- /**
- * A data structure for a patch entry.
- */
- struct patch_entry {
- int type;
- int len;
- unsigned char data[PATCH_MAX_LEN];
- };
- /**
- * A structure for patch context
- */
- struct patch_ctx {
- int dev;
- int fd;
- int patch_error;
- int reset_enable_patch;
- };
- /**
- * Send HCI command to the controller
- */
- static int intel_write_cmd(int dev, unsigned char *buf, int len)
- {
- int ret;
- PRINT_PACKET(buf, len, "<----- SEND CMD: ");
- ret = write(dev, buf, len);
- if (ret < 0)
- return -errno;
- if (ret != len)
- return -1;
- return ret;
- }
- /**
- * Read the event from the controller
- */
- static int intel_read_evt(int dev, unsigned char *buf, int len)
- {
- int ret;
- ret = read_hci_event(dev, buf, len);
- if (ret < 0)
- return -1;
- PRINT_PACKET(buf, ret, "-----> READ EVT: ");
- return ret;
- }
- /**
- * Validate HCI events
- */
- static int validate_events(struct patch_entry *event,
- struct patch_entry *entry)
- {
- if (event == NULL || entry == NULL) {
- DBGPRINT("invalid patch entry parameters");
- return -1;
- }
- if (event->len != entry->len) {
- DBGPRINT("lengths are mismatched:[%d|%d]",
- event->len, entry->len);
- return -1;
- }
- if (memcmp(event->data, entry->data, event->len)) {
- DBGPRINT("data is mismatched");
- return -1;
- }
- return 0;
- }
- /**
- * Read the next patch entry one line at a time
- */
- static int get_next_patch_entry(int fd, struct patch_entry *entry)
- {
- int size;
- char rb;
- if (read(fd, &rb, 1) <= 0)
- return 0;
- entry->type = rb;
- switch (entry->type) {
- case PATCH_TYPE_CMD:
- entry->data[0] = HCI_COMMAND_PKT;
- if (read(fd, &entry->data[1], 3) < 0)
- return -1;
- size = (int)entry->data[3];
- if (read(fd, &entry->data[4], size) < 0)
- return -1;
- entry->len = HCI_TYPE_LEN + HCI_COMMAND_HDR_SIZE + size;
- break;
- case PATCH_TYPE_EVT:
- entry->data[0] = HCI_EVENT_PKT;
- if (read(fd, &entry->data[1], 2) < 0)
- return -1;
- size = (int)entry->data[2];
- if (read(fd, &entry->data[3], size) < 0)
- return -1;
- entry->len = HCI_TYPE_LEN + HCI_EVENT_HDR_SIZE + size;
- break;
- default:
- fprintf(stderr, "invalid patch entry(%d)\n", entry->type);
- return -1;
- }
- return entry->len;
- }
- /**
- * Download the patch set to the controller and verify the event
- */
- static int intel_download_patch(struct patch_ctx *ctx)
- {
- int ret;
- struct patch_entry entry;
- struct patch_entry event;
- DBGPRINT("start patch downloading");
- do {
- ret = get_next_patch_entry(ctx->fd, &entry);
- if (ret <= 0) {
- ctx->patch_error = 1;
- break;
- }
- switch (entry.type) {
- case PATCH_TYPE_CMD:
- ret = intel_write_cmd(ctx->dev,
- entry.data,
- entry.len);
- if (ret <= 0) {
- fprintf(stderr, "failed to send cmd(%d)\n",
- ret);
- return ret;
- }
- break;
- case PATCH_TYPE_EVT:
- ret = intel_read_evt(ctx->dev, event.data,
- sizeof(event.data));
- if (ret <= 0) {
- fprintf(stderr, "failed to read evt(%d)\n",
- ret);
- return ret;
- }
- event.len = ret;
- if (validate_events(&event, &entry) < 0) {
- DBGPRINT("events are mismatched");
- ctx->patch_error = 1;
- return -1;
- }
- break;
- default:
- fprintf(stderr, "unknown patch type(%d)\n",
- entry.type);
- return -1;
- }
- } while (1);
- return ret;
- }
- static int open_patch_file(struct patch_ctx *ctx, char *fw_ver)
- {
- char patch_file[PATH_MAX];
- snprintf(patch_file, PATH_MAX, "%s%s%s", PATCH_FILE_PATH,
- fw_ver, PATCH_SEQ_EXT);
- DBGPRINT("PATCH_FILE: %s", patch_file);
- ctx->fd = open(patch_file, O_RDONLY);
- if (ctx->fd < 0) {
- DBGPRINT("cannot open patch file. go to post patch");
- return -1;
- }
- return 0;
- }
- /**
- * Prepare the controller for patching.
- */
- static int pre_patch(struct patch_ctx *ctx)
- {
- int ret, i;
- struct patch_entry entry;
- char fw_ver[INTEL_VER_PARAM_LEN * 2];
- DBGPRINT("start pre_patch");
- entry.data[0] = HCI_COMMAND_PKT;
- entry.data[1] = 0x11;
- entry.data[2] = 0xFC;
- entry.data[3] = 0x02;
- entry.data[4] = 0x01;
- entry.data[5] = 0x00;
- entry.len = HCI_TYPE_LEN + HCI_COMMAND_HDR_SIZE + INTEL_MFG_PARAM_LEN;
- ret = intel_write_cmd(ctx->dev, entry.data, entry.len);
- if (ret < 0) {
- fprintf(stderr, "failed to send cmd(%d)\n", ret);
- return ret;
- }
- ret = intel_read_evt(ctx->dev, entry.data, sizeof(entry.data));
- if (ret < 0) {
- fprintf(stderr, "failed to read evt(%d)\n", ret);
- return ret;
- }
- entry.len = ret;
- if (entry.data[6] != 0x00) {
- DBGPRINT("command failed. status=%02x", entry.data[6]);
- ctx->patch_error = 1;
- return -1;
- }
- entry.data[0] = HCI_COMMAND_PKT;
- entry.data[1] = 0x05;
- entry.data[2] = 0xFC;
- entry.data[3] = 0x00;
- entry.len = HCI_TYPE_LEN + HCI_COMMAND_HDR_SIZE;
- ret = intel_write_cmd(ctx->dev, entry.data, entry.len);
- if (ret < 0) {
- fprintf(stderr, "failed to send cmd(%d)\n", ret);
- return ret;
- }
- ret = intel_read_evt(ctx->dev, entry.data, sizeof(entry.data));
- if (ret < 0) {
- fprintf(stderr, "failed to read evt(%d)\n", ret);
- return ret;
- }
- entry.len = ret;
- if (entry.data[6] != 0x00) {
- DBGPRINT("command failed. status=%02x", entry.data[6]);
- ctx->patch_error = 1;
- return -1;
- }
- for (i = 0; i < INTEL_VER_PARAM_LEN; i++)
- sprintf(&fw_ver[i*2], "%02x", entry.data[7+i]);
- if (open_patch_file(ctx, fw_ver) < 0) {
- ctx->patch_error = 1;
- return -1;
- }
- return ret;
- }
- /*
- * check the event is startup event
- */
- static int is_startup_evt(unsigned char *buf)
- {
- if (buf[1] == 0xFF && buf[2] == 0x01 && buf[3] == 0x00)
- return 1;
- return 0;
- }
- /**
- * Finalize the patch process and reset the controller
- */
- static int post_patch(struct patch_ctx *ctx)
- {
- int ret;
- struct patch_entry entry;
- DBGPRINT("start post_patch");
- entry.data[0] = HCI_COMMAND_PKT;
- entry.data[1] = 0x11;
- entry.data[2] = 0xFC;
- entry.data[3] = 0x02;
- entry.data[4] = 0x00;
- if (ctx->reset_enable_patch)
- entry.data[5] = 0x02;
- else
- entry.data[5] = 0x01;
- entry.len = HCI_TYPE_LEN + HCI_COMMAND_HDR_SIZE + INTEL_MFG_PARAM_LEN;
- ret = intel_write_cmd(ctx->dev, entry.data, entry.len);
- if (ret < 0) {
- fprintf(stderr, "failed to send cmd(%d)\n", ret);
- return ret;
- }
- ret = intel_read_evt(ctx->dev, entry.data, sizeof(entry.data));
- if (ret < 0) {
- fprintf(stderr, "failed to read evt(%d)\n", ret);
- return ret;
- }
- entry.len = ret;
- if (entry.data[6] != 0x00) {
- fprintf(stderr, "cmd failed. st=%02x\n", entry.data[6]);
- return -1;
- }
- do {
- ret = intel_read_evt(ctx->dev, entry.data,
- sizeof(entry.data));
- if (ret < 0) {
- fprintf(stderr, "failed to read cmd(%d)\n", ret);
- return ret;
- }
- entry.len = ret;
- } while (!is_startup_evt(entry.data));
- return ret;
- }
- /**
- * Main routine that handles the device patching process.
- */
- static int intel_patch_device(struct patch_ctx *ctx)
- {
- int ret;
- ret = pre_patch(ctx);
- if (ret < 0) {
- if (!ctx->patch_error) {
- fprintf(stderr, "I/O error: pre_patch failed\n");
- return ret;
- }
- DBGPRINT("patch failed. proceed to post patch");
- goto post_patch;
- }
- ret = intel_download_patch(ctx);
- if (ret < 0) {
- if (!ctx->patch_error) {
- fprintf(stderr, "I/O error: download_patch failed\n");
- close(ctx->fd);
- return ret;
- }
- } else {
- DBGPRINT("patch done");
- ctx->reset_enable_patch = 1;
- }
- close(ctx->fd);
- post_patch:
- ret = post_patch(ctx);
- if (ret < 0) {
- fprintf(stderr, "post_patch failed(%d)\n", ret);
- return ret;
- }
- return 0;
- }
- static int set_rts(int dev, int rtsval)
- {
- int arg;
- if (ioctl(dev, TIOCMGET, &arg) < 0) {
- perror("cannot get TIOCMGET");
- return -errno;
- }
- if (rtsval)
- arg |= TIOCM_RTS;
- else
- arg &= ~TIOCM_RTS;
- if (ioctl(dev, TIOCMSET, &arg) == -1) {
- perror("cannot set TIOCMGET");
- return -errno;
- }
- return 0;
- }
- static unsigned char get_intel_speed(int speed)
- {
- switch (speed) {
- case 9600:
- return 0x00;
- case 19200:
- return 0x01;
- case 38400:
- return 0x02;
- case 57600:
- return 0x03;
- case 115200:
- return 0x04;
- case 230400:
- return 0x05;
- case 460800:
- return 0x06;
- case 921600:
- return 0x07;
- case 1843200:
- return 0x08;
- case 3250000:
- return 0x09;
- case 2000000:
- return 0x0A;
- case 3000000:
- return 0x0B;
- default:
- return 0xFF;
- }
- }
- /**
- * if it failed to change to new baudrate, it will rollback
- * to initial baudrate
- */
- static int change_baudrate(int dev, int init_speed, int *speed,
- struct termios *ti)
- {
- int ret;
- unsigned char br;
- unsigned char cmd[5];
- unsigned char evt[7];
- DBGPRINT("start baudrate change");
- ret = set_rts(dev, 0);
- if (ret < 0) {
- fprintf(stderr, "failed to clear RTS\n");
- return ret;
- }
- cmd[0] = HCI_COMMAND_PKT;
- cmd[1] = 0x06;
- cmd[2] = 0xFC;
- cmd[3] = 0x01;
- br = get_intel_speed(*speed);
- if (br == 0xFF) {
- fprintf(stderr, "speed %d is not supported\n", *speed);
- return -1;
- }
- cmd[4] = br;
- ret = intel_write_cmd(dev, cmd, sizeof(cmd));
- if (ret < 0) {
- fprintf(stderr, "failed to send cmd(%d)\n", ret);
- return ret;
- }
- /*
- * wait for buffer to be consumed by the controller
- */
- usleep(300000);
- if (set_speed(dev, ti, *speed) < 0) {
- fprintf(stderr, "can't set to new baud rate\n");
- return -1;
- }
- ret = set_rts(dev, 1);
- if (ret < 0) {
- fprintf(stderr, "failed to set RTS\n");
- return ret;
- }
- ret = intel_read_evt(dev, evt, sizeof(evt));
- if (ret < 0) {
- fprintf(stderr, "failed to read evt(%d)\n", ret);
- return ret;
- }
- if (evt[4] != 0x00) {
- fprintf(stderr,
- "failed to change speed. use default speed %d\n",
- init_speed);
- *speed = init_speed;
- }
- return 0;
- }
- /**
- * An entry point for Intel specific initialization
- */
- int intel_init(int dev, int init_speed, int *speed, struct termios *ti)
- {
- int ret = 0;
- struct patch_ctx ctx;
- if (change_baudrate(dev, init_speed, speed, ti) < 0)
- return -1;
- ctx.dev = dev;
- ctx.patch_error = 0;
- ctx.reset_enable_patch = 0;
- ret = intel_patch_device(&ctx);
- if (ret < 0)
- fprintf(stderr, "failed to initialize the device");
- return ret;
- }
|