1 /* 2 * 3 * BlueZ - Bluetooth protocol stack for Linux 4 * 5 * Copyright (C) 2005-2010 Marcel Holtmann <marcel (at) holtmann.org> 6 * 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 21 * 22 */ 23 24 #ifdef HAVE_CONFIG_H 25 #include <config.h> 26 #endif 27 28 #include <stdio.h> 29 #include <errno.h> 30 #include <fcntl.h> 31 #include <unistd.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <signal.h> 35 #include <syslog.h> 36 #include <termios.h> 37 #include <time.h> 38 #include <sys/time.h> 39 #include <sys/poll.h> 40 #include <sys/param.h> 41 #include <sys/ioctl.h> 42 #include <sys/socket.h> 43 #include <sys/uio.h> 44 45 #include <bluetooth/bluetooth.h> 46 #include <bluetooth/hci.h> 47 #include <bluetooth/hci_lib.h> 48 49 #include "hciattach.h" 50 51 #define FAILIF(x, args...) do { \ 52 if (x) { \ 53 fprintf(stderr, ##args); \ 54 return -1; \ 55 } \ 56 } while(0) 57 58 typedef struct { 59 uint8_t uart_prefix; 60 hci_event_hdr hci_hdr; 61 evt_cmd_complete cmd_complete; 62 uint8_t status; 63 uint8_t data[16]; 64 } __attribute__((packed)) command_complete_t; 65 66 static int read_command_complete(int fd, unsigned short opcode, unsigned char len) { 67 command_complete_t resp; 68 /* Read reply. */ 69 FAILIF(read_hci_event(fd, (unsigned char *)&resp, sizeof(resp)) < 0, 70 "Failed to read response"); 71 72 /* Parse speed-change reply */ 73 FAILIF(resp.uart_prefix != HCI_EVENT_PKT, 74 "Error in response: not an event packet, but 0x%02x!\n", 75 resp.uart_prefix); 76 77 FAILIF(resp.hci_hdr.evt != EVT_CMD_COMPLETE, /* event must be event-complete */ 78 "Error in response: not a cmd-complete event, " 79 "but 0x%02x!\n", resp.hci_hdr.evt); 80 81 FAILIF(resp.hci_hdr.plen < 4, /* plen >= 4 for EVT_CMD_COMPLETE */ 82 "Error in response: plen is not >= 4, but 0x%02x!\n", 83 resp.hci_hdr.plen); 84 85 /* cmd-complete event: opcode */ 86 FAILIF(resp.cmd_complete.opcode != (uint16_t)opcode, 87 "Error in response: opcode is 0x%04x, not 0x%04x!", 88 resp.cmd_complete.opcode, opcode); 89 90 return resp.status == 0 ? 0 : -1; 91 } 92 93 typedef struct { 94 uint8_t uart_prefix; 95 hci_command_hdr hci_hdr; 96 uint32_t speed; 97 } __attribute__((packed)) texas_speed_change_cmd_t; 98 99 static int texas_change_speed(int fd, struct termios *ti, uint32_t speed) 100 { 101 /* Send a speed-change request */ 102 texas_speed_change_cmd_t cmd; 103 int n; 104 105 cmd.uart_prefix = HCI_COMMAND_PKT; 106 cmd.hci_hdr.opcode = 0xff36; 107 cmd.hci_hdr.plen = sizeof(uint32_t); 108 cmd.speed = speed; 109 110 fprintf(stderr, "Setting speed to %d\n", speed); 111 n = write(fd, &cmd, sizeof(cmd)); 112 if (n < 0) { 113 perror("Failed to write speed-set command"); 114 return -1; 115 } 116 if (n < (int)sizeof(cmd)) { 117 fprintf(stderr, "Wanted to write %d bytes, could only write %d. " 118 "Stop\n", (int)sizeof(cmd), n); 119 return -1; 120 } 121 /* Parse speed-change reply */ 122 if (read_command_complete(fd, cmd.hci_hdr.opcode, cmd.hci_hdr.plen) < 0) { 123 return -1; 124 } 125 if (set_speed(fd, ti, speed) < 0) { 126 perror("Can't set baud rate"); 127 return -1; 128 } 129 return 0; 130 } 131 132 static int texas_load_firmware(int fd, const char *firmware) { 133 134 int fw = open(firmware, O_RDONLY); 135 136 fprintf(stdout, "Opening firmware file: %s\n", firmware); 137 138 FAILIF(fw < 0, 139 "Could not open firmware file %s: %s (%d).\n", 140 firmware, strerror(errno), errno); 141 142 fprintf(stdout, "Uploading firmware...\n"); 143 do { 144 /* Read each command and wait for a response. */ 145 unsigned char data[1024]; 146 unsigned char cmdp[1 + sizeof(hci_command_hdr)]; 147 hci_command_hdr *cmd = (hci_command_hdr *)(cmdp + 1); 148 int nr; 149 nr = read(fw, cmdp, sizeof(cmdp)); 150 if (!nr) 151 break; 152 FAILIF(nr != sizeof(cmdp), "Could not read H4 + HCI header!\n"); 153 FAILIF(*cmdp != HCI_COMMAND_PKT, "Command is not an H4 command packet!\n"); 154 155 FAILIF(read(fw, data, cmd->plen) != cmd->plen, 156 "Could not read %d bytes of data for command with opcode %04x!\n", 157 cmd->plen, 158 cmd->opcode); 159 160 { 161 int nw; 162 #if 0 163 fprintf(stdout, "\topcode 0x%04x (%d bytes of data).\n", 164 cmd->opcode, 165 cmd->plen); 166 #endif 167 struct iovec iov_cmd[2]; 168 iov_cmd[0].iov_base = cmdp; 169 iov_cmd[0].iov_len = sizeof(cmdp); 170 iov_cmd[1].iov_base = data; 171 iov_cmd[1].iov_len = cmd->plen; 172 nw = writev(fd, iov_cmd, 2); 173 FAILIF(nw != (int) sizeof(cmd) + cmd->plen, 174 "Could not send entire command (sent only %d bytes)!\n", 175 nw); 176 } 177 178 /* Wait for response */ 179 if (read_command_complete(fd, 180 cmd->opcode, 181 cmd->plen) < 0) { 182 return -1; 183 } 184 185 } while(1); 186 fprintf(stdout, "Firmware upload successful.\n"); 187 188 close(fw); 189 return 0; 190 } 191 192 int texasalt_init(int fd, int speed, struct termios *ti) 193 { 194 struct timespec tm = {0, 50000}; 195 char cmd[4]; 196 unsigned char resp[100]; /* Response */ 197 int n; 198 199 memset(resp,'\0', 100); 200 201 /* It is possible to get software version with manufacturer specific 202 HCI command HCI_VS_TI_Version_Number. But the only thing you get more 203 is if this is point-to-point or point-to-multipoint module */ 204 205 /* Get Manufacturer and LMP version */ 206 cmd[0] = HCI_COMMAND_PKT; 207 cmd[1] = 0x01; 208 cmd[2] = 0x10; 209 cmd[3] = 0x00; 210 211 do { 212 n = write(fd, cmd, 4); 213 if (n < 0) { 214 perror("Failed to write init command (READ_LOCAL_VERSION_INFORMATION)"); 215 return -1; 216 } 217 if (n < 4) { 218 fprintf(stderr, "Wanted to write 4 bytes, could only write %d. Stop\n", n); 219 return -1; 220 } 221 222 /* Read reply. */ 223 if (read_hci_event(fd, resp, 100) < 0) { 224 perror("Failed to read init response (READ_LOCAL_VERSION_INFORMATION)"); 225 return -1; 226 } 227 228 /* Wait for command complete event for our Opcode */ 229 } while (resp[4] != cmd[1] && resp[5] != cmd[2]); 230 231 /* Verify manufacturer */ 232 if ((resp[11] & 0xFF) != 0x0d) 233 fprintf(stderr,"WARNING : module's manufacturer is not Texas Instrument\n"); 234 235 /* Print LMP version */ 236 fprintf(stderr, "Texas module LMP version : 0x%02x\n", resp[10] & 0xFF); 237 238 /* Print LMP subversion */ 239 { 240 unsigned short lmp_subv = resp[13] | (resp[14] << 8); 241 unsigned short brf_chip = (lmp_subv & 0x7c00) >> 10; 242 static const char *c_brf_chip[8] = { 243 "unknown", 244 "unknown", 245 "brf6100", 246 "brf6150", 247 "brf6300", 248 "brf6350", 249 "unknown", 250 "wl1271" 251 }; 252 char fw[100]; 253 254 fprintf(stderr, "Texas module LMP sub-version : 0x%04x\n", lmp_subv); 255 256 fprintf(stderr, 257 "\tinternal version freeze: %d\n" 258 "\tsoftware version: %d\n" 259 "\tchip: %s (%d)\n", 260 lmp_subv & 0x7f, 261 ((lmp_subv & 0x8000) >> (15-3)) | ((lmp_subv & 0x380) >> 7), 262 ((brf_chip > 7) ? "unknown" : c_brf_chip[brf_chip]), 263 brf_chip); 264 265 sprintf(fw, "/etc/firmware/%s.bin", c_brf_chip[brf_chip]); 266 texas_change_speed(fd, ti, speed); 267 texas_load_firmware(fd, fw); 268 } 269 nanosleep(&tm, NULL); 270 return 0; 271 } 272