diff options
author | Bala Shanmugam <sbalashanmugam@atheros.com> | 2011-02-11 05:08:53 -0500 |
---|---|---|
committer | Gustavo F. Padovan <padovan@profusion.mobi> | 2011-02-11 12:21:46 -0500 |
commit | d9f51b51db2064c9049bf7924318fd8c6ed852cb (patch) | |
tree | fe8b2d01f424168cc110d92d9d23e9a8a8f4813f /drivers/bluetooth/ath3k.c | |
parent | e3fb592b15602196d38b225dc78aab8d631a5f89 (diff) |
Bluetooth: Add firmware support for Atheros 3012
Blacklisted AR3012 PID in btusb and added the same
in ath3k to load patch and sysconfig files.
Signed-off-by: Bala Shanmugam <sbalashanmugam@atheros.com>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
Diffstat (limited to 'drivers/bluetooth/ath3k.c')
-rw-r--r-- | drivers/bluetooth/ath3k.c | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c index 41dadacd3d15..e6acaba1e45c 100644 --- a/drivers/bluetooth/ath3k.c +++ b/drivers/bluetooth/ath3k.c | |||
@@ -31,6 +31,30 @@ | |||
31 | 31 | ||
32 | #define VERSION "1.0" | 32 | #define VERSION "1.0" |
33 | 33 | ||
34 | #define ATH3K_DNLOAD 0x01 | ||
35 | #define ATH3K_GETSTATE 0x05 | ||
36 | #define ATH3K_SET_NORMAL_MODE 0x07 | ||
37 | #define ATH3K_GETVERSION 0x09 | ||
38 | #define USB_REG_SWITCH_VID_PID 0x0a | ||
39 | |||
40 | #define ATH3K_MODE_MASK 0x3F | ||
41 | #define ATH3K_NORMAL_MODE 0x0E | ||
42 | |||
43 | #define ATH3K_PATCH_UPDATE 0x80 | ||
44 | #define ATH3K_SYSCFG_UPDATE 0x40 | ||
45 | |||
46 | #define ATH3K_XTAL_FREQ_26M 0x00 | ||
47 | #define ATH3K_XTAL_FREQ_40M 0x01 | ||
48 | #define ATH3K_XTAL_FREQ_19P2 0x02 | ||
49 | #define ATH3K_NAME_LEN 0xFF | ||
50 | |||
51 | struct ath3k_version { | ||
52 | unsigned int rom_version; | ||
53 | unsigned int build_version; | ||
54 | unsigned int ram_version; | ||
55 | unsigned char ref_clock; | ||
56 | unsigned char reserved[0x07]; | ||
57 | }; | ||
34 | 58 | ||
35 | static struct usb_device_id ath3k_table[] = { | 59 | static struct usb_device_id ath3k_table[] = { |
36 | /* Atheros AR3011 */ | 60 | /* Atheros AR3011 */ |
@@ -41,13 +65,29 @@ static struct usb_device_id ath3k_table[] = { | |||
41 | 65 | ||
42 | /* Atheros AR9285 Malbec with sflash firmware */ | 66 | /* Atheros AR9285 Malbec with sflash firmware */ |
43 | { USB_DEVICE(0x03F0, 0x311D) }, | 67 | { USB_DEVICE(0x03F0, 0x311D) }, |
68 | |||
69 | /* Atheros AR3012 with sflash firmware*/ | ||
70 | { USB_DEVICE(0x0CF3, 0x3004) }, | ||
71 | |||
44 | { } /* Terminating entry */ | 72 | { } /* Terminating entry */ |
45 | }; | 73 | }; |
46 | 74 | ||
47 | MODULE_DEVICE_TABLE(usb, ath3k_table); | 75 | MODULE_DEVICE_TABLE(usb, ath3k_table); |
48 | 76 | ||
77 | #define BTUSB_ATH3012 0x80 | ||
78 | /* This table is to load patch and sysconfig files | ||
79 | * for AR3012 */ | ||
80 | static struct usb_device_id ath3k_blist_tbl[] = { | ||
81 | |||
82 | /* Atheros AR3012 with sflash firmware*/ | ||
83 | { USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 }, | ||
84 | |||
85 | { } /* Terminating entry */ | ||
86 | }; | ||
87 | |||
49 | #define USB_REQ_DFU_DNLOAD 1 | 88 | #define USB_REQ_DFU_DNLOAD 1 |
50 | #define BULK_SIZE 4096 | 89 | #define BULK_SIZE 4096 |
90 | #define FW_HDR_SIZE 20 | ||
51 | 91 | ||
52 | static int ath3k_load_firmware(struct usb_device *udev, | 92 | static int ath3k_load_firmware(struct usb_device *udev, |
53 | const struct firmware *firmware) | 93 | const struct firmware *firmware) |
@@ -103,6 +143,215 @@ error: | |||
103 | return err; | 143 | return err; |
104 | } | 144 | } |
105 | 145 | ||
146 | static int ath3k_get_state(struct usb_device *udev, unsigned char *state) | ||
147 | { | ||
148 | int pipe = 0; | ||
149 | |||
150 | pipe = usb_rcvctrlpipe(udev, 0); | ||
151 | return usb_control_msg(udev, pipe, ATH3K_GETSTATE, | ||
152 | USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, | ||
153 | state, 0x01, USB_CTRL_SET_TIMEOUT); | ||
154 | } | ||
155 | |||
156 | static int ath3k_get_version(struct usb_device *udev, | ||
157 | struct ath3k_version *version) | ||
158 | { | ||
159 | int pipe = 0; | ||
160 | |||
161 | pipe = usb_rcvctrlpipe(udev, 0); | ||
162 | return usb_control_msg(udev, pipe, ATH3K_GETVERSION, | ||
163 | USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, version, | ||
164 | sizeof(struct ath3k_version), | ||
165 | USB_CTRL_SET_TIMEOUT); | ||
166 | } | ||
167 | |||
168 | static int ath3k_load_fwfile(struct usb_device *udev, | ||
169 | const struct firmware *firmware) | ||
170 | { | ||
171 | u8 *send_buf; | ||
172 | int err, pipe, len, size, count, sent = 0; | ||
173 | int ret; | ||
174 | |||
175 | count = firmware->size; | ||
176 | |||
177 | send_buf = kmalloc(BULK_SIZE, GFP_ATOMIC); | ||
178 | if (!send_buf) { | ||
179 | BT_ERR("Can't allocate memory chunk for firmware"); | ||
180 | return -ENOMEM; | ||
181 | } | ||
182 | |||
183 | size = min_t(uint, count, FW_HDR_SIZE); | ||
184 | memcpy(send_buf, firmware->data, size); | ||
185 | |||
186 | pipe = usb_sndctrlpipe(udev, 0); | ||
187 | ret = usb_control_msg(udev, pipe, ATH3K_DNLOAD, | ||
188 | USB_TYPE_VENDOR, 0, 0, send_buf, | ||
189 | size, USB_CTRL_SET_TIMEOUT); | ||
190 | if (ret < 0) { | ||
191 | BT_ERR("Can't change to loading configuration err"); | ||
192 | kfree(send_buf); | ||
193 | return ret; | ||
194 | } | ||
195 | |||
196 | sent += size; | ||
197 | count -= size; | ||
198 | |||
199 | while (count) { | ||
200 | size = min_t(uint, count, BULK_SIZE); | ||
201 | pipe = usb_sndbulkpipe(udev, 0x02); | ||
202 | |||
203 | memcpy(send_buf, firmware->data + sent, size); | ||
204 | |||
205 | err = usb_bulk_msg(udev, pipe, send_buf, size, | ||
206 | &len, 3000); | ||
207 | if (err || (len != size)) { | ||
208 | BT_ERR("Error in firmware loading err = %d," | ||
209 | "len = %d, size = %d", err, len, size); | ||
210 | kfree(send_buf); | ||
211 | return err; | ||
212 | } | ||
213 | sent += size; | ||
214 | count -= size; | ||
215 | } | ||
216 | |||
217 | kfree(send_buf); | ||
218 | return 0; | ||
219 | } | ||
220 | |||
221 | static int ath3k_switch_pid(struct usb_device *udev) | ||
222 | { | ||
223 | int pipe = 0; | ||
224 | |||
225 | pipe = usb_sndctrlpipe(udev, 0); | ||
226 | return usb_control_msg(udev, pipe, USB_REG_SWITCH_VID_PID, | ||
227 | USB_TYPE_VENDOR, 0, 0, | ||
228 | NULL, 0, USB_CTRL_SET_TIMEOUT); | ||
229 | } | ||
230 | |||
231 | static int ath3k_set_normal_mode(struct usb_device *udev) | ||
232 | { | ||
233 | unsigned char fw_state; | ||
234 | int pipe = 0, ret; | ||
235 | |||
236 | ret = ath3k_get_state(udev, &fw_state); | ||
237 | if (ret < 0) { | ||
238 | BT_ERR("Can't get state to change to normal mode err"); | ||
239 | return ret; | ||
240 | } | ||
241 | |||
242 | if ((fw_state & ATH3K_MODE_MASK) == ATH3K_NORMAL_MODE) { | ||
243 | BT_DBG("firmware was already in normal mode"); | ||
244 | return 0; | ||
245 | } | ||
246 | |||
247 | pipe = usb_sndctrlpipe(udev, 0); | ||
248 | return usb_control_msg(udev, pipe, ATH3K_SET_NORMAL_MODE, | ||
249 | USB_TYPE_VENDOR, 0, 0, | ||
250 | NULL, 0, USB_CTRL_SET_TIMEOUT); | ||
251 | } | ||
252 | |||
253 | static int ath3k_load_patch(struct usb_device *udev) | ||
254 | { | ||
255 | unsigned char fw_state; | ||
256 | char filename[ATH3K_NAME_LEN] = {0}; | ||
257 | const struct firmware *firmware; | ||
258 | struct ath3k_version fw_version, pt_version; | ||
259 | int ret; | ||
260 | |||
261 | ret = ath3k_get_state(udev, &fw_state); | ||
262 | if (ret < 0) { | ||
263 | BT_ERR("Can't get state to change to load ram patch err"); | ||
264 | return ret; | ||
265 | } | ||
266 | |||
267 | if (fw_state & ATH3K_PATCH_UPDATE) { | ||
268 | BT_DBG("Patch was already downloaded"); | ||
269 | return 0; | ||
270 | } | ||
271 | |||
272 | ret = ath3k_get_version(udev, &fw_version); | ||
273 | if (ret < 0) { | ||
274 | BT_ERR("Can't get version to change to load ram patch err"); | ||
275 | return ret; | ||
276 | } | ||
277 | |||
278 | snprintf(filename, ATH3K_NAME_LEN, "ar3k/AthrBT_0x%08x.dfu", | ||
279 | fw_version.rom_version); | ||
280 | |||
281 | ret = request_firmware(&firmware, filename, &udev->dev); | ||
282 | if (ret < 0) { | ||
283 | BT_ERR("Patch file not found %s", filename); | ||
284 | return ret; | ||
285 | } | ||
286 | |||
287 | pt_version.rom_version = *(int *)(firmware->data + firmware->size - 8); | ||
288 | pt_version.build_version = *(int *) | ||
289 | (firmware->data + firmware->size - 4); | ||
290 | |||
291 | if ((pt_version.rom_version != fw_version.rom_version) || | ||
292 | (pt_version.build_version <= fw_version.build_version)) { | ||
293 | BT_ERR("Patch file version did not match with firmware"); | ||
294 | release_firmware(firmware); | ||
295 | return -EINVAL; | ||
296 | } | ||
297 | |||
298 | ret = ath3k_load_fwfile(udev, firmware); | ||
299 | release_firmware(firmware); | ||
300 | |||
301 | return ret; | ||
302 | } | ||
303 | |||
304 | static int ath3k_load_syscfg(struct usb_device *udev) | ||
305 | { | ||
306 | unsigned char fw_state; | ||
307 | char filename[ATH3K_NAME_LEN] = {0}; | ||
308 | const struct firmware *firmware; | ||
309 | struct ath3k_version fw_version; | ||
310 | int clk_value, ret; | ||
311 | |||
312 | ret = ath3k_get_state(udev, &fw_state); | ||
313 | if (ret < 0) { | ||
314 | BT_ERR("Can't get state to change to load configration err"); | ||
315 | return -EBUSY; | ||
316 | } | ||
317 | |||
318 | ret = ath3k_get_version(udev, &fw_version); | ||
319 | if (ret < 0) { | ||
320 | BT_ERR("Can't get version to change to load ram patch err"); | ||
321 | return ret; | ||
322 | } | ||
323 | |||
324 | switch (fw_version.ref_clock) { | ||
325 | |||
326 | case ATH3K_XTAL_FREQ_26M: | ||
327 | clk_value = 26; | ||
328 | break; | ||
329 | case ATH3K_XTAL_FREQ_40M: | ||
330 | clk_value = 40; | ||
331 | break; | ||
332 | case ATH3K_XTAL_FREQ_19P2: | ||
333 | clk_value = 19; | ||
334 | break; | ||
335 | default: | ||
336 | clk_value = 0; | ||
337 | break; | ||
338 | } | ||
339 | |||
340 | snprintf(filename, ATH3K_NAME_LEN, "ar3k/ramps_0x%08x_%d%s", | ||
341 | fw_version.rom_version, clk_value, ".dfu"); | ||
342 | |||
343 | ret = request_firmware(&firmware, filename, &udev->dev); | ||
344 | if (ret < 0) { | ||
345 | BT_ERR("Configuration file not found %s", filename); | ||
346 | return ret; | ||
347 | } | ||
348 | |||
349 | ret = ath3k_load_fwfile(udev, firmware); | ||
350 | release_firmware(firmware); | ||
351 | |||
352 | return ret; | ||
353 | } | ||
354 | |||
106 | static int ath3k_probe(struct usb_interface *intf, | 355 | static int ath3k_probe(struct usb_interface *intf, |
107 | const struct usb_device_id *id) | 356 | const struct usb_device_id *id) |
108 | { | 357 | { |
@@ -115,7 +364,37 @@ static int ath3k_probe(struct usb_interface *intf, | |||
115 | if (intf->cur_altsetting->desc.bInterfaceNumber != 0) | 364 | if (intf->cur_altsetting->desc.bInterfaceNumber != 0) |
116 | return -ENODEV; | 365 | return -ENODEV; |
117 | 366 | ||
367 | /* match device ID in ath3k blacklist table */ | ||
368 | if (!id->driver_info) { | ||
369 | const struct usb_device_id *match; | ||
370 | match = usb_match_id(intf, ath3k_blist_tbl); | ||
371 | if (match) | ||
372 | id = match; | ||
373 | } | ||
374 | |||
375 | /* load patch and sysconfig files for AR3012 */ | ||
376 | if (id->driver_info & BTUSB_ATH3012) { | ||
377 | ret = ath3k_load_patch(udev); | ||
378 | if (ret < 0) { | ||
379 | BT_ERR("Loading patch file failed"); | ||
380 | return ret; | ||
381 | } | ||
382 | ret = ath3k_load_syscfg(udev); | ||
383 | if (ret < 0) { | ||
384 | BT_ERR("Loading sysconfig file failed"); | ||
385 | return ret; | ||
386 | } | ||
387 | ret = ath3k_set_normal_mode(udev); | ||
388 | if (ret < 0) { | ||
389 | BT_ERR("Set normal mode failed"); | ||
390 | return ret; | ||
391 | } | ||
392 | ath3k_switch_pid(udev); | ||
393 | return 0; | ||
394 | } | ||
395 | |||
118 | if (request_firmware(&firmware, "ath3k-1.fw", &udev->dev) < 0) { | 396 | if (request_firmware(&firmware, "ath3k-1.fw", &udev->dev) < 0) { |
397 | BT_ERR("Error loading firmware"); | ||
119 | return -EIO; | 398 | return -EIO; |
120 | } | 399 | } |
121 | 400 | ||