diff options
author | Vikram Kandukuri <vkandukuri@atheros.com> | 2010-01-06 08:34:15 -0500 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2010-01-30 08:57:34 -0500 |
commit | 9670d80a9a6e24725c4111bef5d6cc7786ad0dc5 (patch) | |
tree | 22cb112a77bb85cb297cb68bb5ac360b62358b61 /drivers/bluetooth/ath3k.c | |
parent | 7427847d2d044d85f478757ea03165ae3cfe87c4 (diff) |
Bluetooth: Add DFU driver for Atheros Bluetooth chipset AR3011
Signed-off-by: Vikram Kandukuri <vikram.kandukuri@atheros.com>
Signed-off-by: Alicke Xu <sxu@atheros.com>
Reviewed-by: Luis R. Rodriguez <lrodriguez@atheros.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'drivers/bluetooth/ath3k.c')
-rw-r--r-- | drivers/bluetooth/ath3k.c | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c new file mode 100644 index 000000000000..add9485ca5b6 --- /dev/null +++ b/drivers/bluetooth/ath3k.c | |||
@@ -0,0 +1,187 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2008-2009 Atheros Communications Inc. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; if not, write to the Free Software | ||
16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
17 | * | ||
18 | */ | ||
19 | |||
20 | |||
21 | #include <linux/module.h> | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <linux/types.h> | ||
26 | #include <linux/errno.h> | ||
27 | #include <linux/device.h> | ||
28 | #include <linux/firmware.h> | ||
29 | #include <linux/usb.h> | ||
30 | #include <net/bluetooth/bluetooth.h> | ||
31 | |||
32 | #define VERSION "1.0" | ||
33 | |||
34 | |||
35 | static struct usb_device_id ath3k_table[] = { | ||
36 | /* Atheros AR3011 */ | ||
37 | { USB_DEVICE(0x0CF3, 0x3000) }, | ||
38 | { } /* Terminating entry */ | ||
39 | }; | ||
40 | |||
41 | MODULE_DEVICE_TABLE(usb, ath3k_table); | ||
42 | |||
43 | #define USB_REQ_DFU_DNLOAD 1 | ||
44 | #define BULK_SIZE 4096 | ||
45 | |||
46 | struct ath3k_data { | ||
47 | struct usb_device *udev; | ||
48 | u8 *fw_data; | ||
49 | u32 fw_size; | ||
50 | u32 fw_sent; | ||
51 | }; | ||
52 | |||
53 | static int ath3k_load_firmware(struct ath3k_data *data, | ||
54 | unsigned char *firmware, | ||
55 | int count) | ||
56 | { | ||
57 | u8 *send_buf; | ||
58 | int err, pipe, len, size, sent = 0; | ||
59 | |||
60 | BT_DBG("ath3k %p udev %p", data, data->udev); | ||
61 | |||
62 | pipe = usb_sndctrlpipe(data->udev, 0); | ||
63 | |||
64 | if ((usb_control_msg(data->udev, pipe, | ||
65 | USB_REQ_DFU_DNLOAD, | ||
66 | USB_TYPE_VENDOR, 0, 0, | ||
67 | firmware, 20, USB_CTRL_SET_TIMEOUT)) < 0) { | ||
68 | BT_ERR("Can't change to loading configuration err"); | ||
69 | return -EBUSY; | ||
70 | } | ||
71 | sent += 20; | ||
72 | count -= 20; | ||
73 | |||
74 | send_buf = kmalloc(BULK_SIZE, GFP_ATOMIC); | ||
75 | if (!send_buf) { | ||
76 | BT_ERR("Can't allocate memory chunk for firmware"); | ||
77 | return -ENOMEM; | ||
78 | } | ||
79 | |||
80 | while (count) { | ||
81 | size = min_t(uint, count, BULK_SIZE); | ||
82 | pipe = usb_sndbulkpipe(data->udev, 0x02); | ||
83 | memcpy(send_buf, firmware + sent, size); | ||
84 | |||
85 | err = usb_bulk_msg(data->udev, pipe, send_buf, size, | ||
86 | &len, 3000); | ||
87 | |||
88 | if (err || (len != size)) { | ||
89 | BT_ERR("Error in firmware loading err = %d," | ||
90 | "len = %d, size = %d", err, len, size); | ||
91 | goto error; | ||
92 | } | ||
93 | |||
94 | sent += size; | ||
95 | count -= size; | ||
96 | } | ||
97 | |||
98 | kfree(send_buf); | ||
99 | return 0; | ||
100 | |||
101 | error: | ||
102 | kfree(send_buf); | ||
103 | return err; | ||
104 | } | ||
105 | |||
106 | static int ath3k_probe(struct usb_interface *intf, | ||
107 | const struct usb_device_id *id) | ||
108 | { | ||
109 | const struct firmware *firmware; | ||
110 | struct usb_device *udev = interface_to_usbdev(intf); | ||
111 | struct ath3k_data *data; | ||
112 | int size; | ||
113 | |||
114 | BT_DBG("intf %p id %p", intf, id); | ||
115 | |||
116 | if (intf->cur_altsetting->desc.bInterfaceNumber != 0) | ||
117 | return -ENODEV; | ||
118 | |||
119 | data = kzalloc(sizeof(*data), GFP_KERNEL); | ||
120 | if (!data) | ||
121 | return -ENOMEM; | ||
122 | |||
123 | data->udev = udev; | ||
124 | |||
125 | if (request_firmware(&firmware, "ath3k-1.fw", &udev->dev) < 0) { | ||
126 | kfree(data); | ||
127 | return -EIO; | ||
128 | } | ||
129 | |||
130 | size = max_t(uint, firmware->size, 4096); | ||
131 | data->fw_data = kmalloc(size, GFP_KERNEL); | ||
132 | if (!data->fw_data) { | ||
133 | release_firmware(firmware); | ||
134 | kfree(data); | ||
135 | return -ENOMEM; | ||
136 | } | ||
137 | |||
138 | memcpy(data->fw_data, firmware->data, firmware->size); | ||
139 | data->fw_size = firmware->size; | ||
140 | data->fw_sent = 0; | ||
141 | release_firmware(firmware); | ||
142 | |||
143 | usb_set_intfdata(intf, data); | ||
144 | if (ath3k_load_firmware(data, data->fw_data, data->fw_size)) { | ||
145 | usb_set_intfdata(intf, NULL); | ||
146 | return -EIO; | ||
147 | } | ||
148 | |||
149 | return 0; | ||
150 | } | ||
151 | |||
152 | static void ath3k_disconnect(struct usb_interface *intf) | ||
153 | { | ||
154 | struct ath3k_data *data = usb_get_intfdata(intf); | ||
155 | |||
156 | BT_DBG("ath3k_disconnect intf %p", intf); | ||
157 | |||
158 | kfree(data->fw_data); | ||
159 | kfree(data); | ||
160 | } | ||
161 | |||
162 | static struct usb_driver ath3k_driver = { | ||
163 | .name = "ath3k", | ||
164 | .probe = ath3k_probe, | ||
165 | .disconnect = ath3k_disconnect, | ||
166 | .id_table = ath3k_table, | ||
167 | }; | ||
168 | |||
169 | static int __init ath3k_init(void) | ||
170 | { | ||
171 | BT_INFO("Atheros AR30xx firmware driver ver %s", VERSION); | ||
172 | return usb_register(&ath3k_driver); | ||
173 | } | ||
174 | |||
175 | static void __exit ath3k_exit(void) | ||
176 | { | ||
177 | usb_deregister(&ath3k_driver); | ||
178 | } | ||
179 | |||
180 | module_init(ath3k_init); | ||
181 | module_exit(ath3k_exit); | ||
182 | |||
183 | MODULE_AUTHOR("Atheros Communications"); | ||
184 | MODULE_DESCRIPTION("Atheros AR30xx firmware driver"); | ||
185 | MODULE_VERSION(VERSION); | ||
186 | MODULE_LICENSE("GPL"); | ||
187 | MODULE_FIRMWARE("ath3k-1.fw"); | ||