hw_taipan.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  1. /*
  2. * Lowlevel hardware access for the
  3. * Razer Taipan mouse
  4. *
  5. * Important notice:
  6. * This hardware driver is based on reverse engineering, only.
  7. *
  8. * Copyright (C) 2007-2010 Michael Buesch <m@bues.ch>
  9. * Driver modified for Taipan by Tibor Peluch <messani@gmail.com>
  10. *
  11. * This program is free software; you can redistribute it and/or
  12. * modify it under the terms of the GNU General Public License
  13. * as published by the Free Software Foundation; either version 2
  14. * of the License, or (at your option) any later version.
  15. *
  16. * This program is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU General Public License for more details.
  20. */
  21. #include "hw_taipan.h"
  22. #include "razer_private.h"
  23. #include <errno.h>
  24. #include <stdlib.h>
  25. #include <stdio.h>
  26. #include <stdint.h>
  27. #include <string.h>
  28. enum {
  29. TAIPAN_LED_SCROLL = 0,
  30. TAIPAN_LED_LOGO,
  31. TAIPAN_NR_LEDS,
  32. };
  33. enum { /* Misc constants */
  34. TAIPAN_NR_DPIMAPPINGS = 82,
  35. TAIPAN_NR_AXES = 3,
  36. };
  37. struct taipan_command {
  38. uint8_t status;
  39. uint8_t padding0[4];
  40. be16_t command;
  41. be16_t request;
  42. be16_t value0;
  43. be16_t value1;
  44. uint8_t padding1[75];
  45. uint8_t checksum;
  46. uint8_t padding2;
  47. } _packed;
  48. struct taipan_private {
  49. struct razer_mouse *m;
  50. /* Firmware version number. */
  51. uint16_t fw_version;
  52. /* The currently set LED states. */
  53. bool led_states[TAIPAN_NR_LEDS];
  54. /* The currently set frequency. */
  55. enum razer_mouse_freq frequency;
  56. /* The currently set resolution. */
  57. struct razer_mouse_dpimapping *cur_dpimapping_X;
  58. struct razer_mouse_dpimapping *cur_dpimapping_Y;
  59. struct razer_mouse_profile profile;
  60. struct razer_mouse_dpimapping dpimapping[TAIPAN_NR_DPIMAPPINGS];
  61. struct razer_axis axes[TAIPAN_NR_AXES];
  62. bool commit_pending;
  63. };
  64. static void taipan_command_init(struct taipan_command *cmd)
  65. {
  66. memset(cmd, 0, sizeof(*cmd));
  67. }
  68. static int taipan_usb_write(struct taipan_private *priv,
  69. int request, int command,
  70. void *buf, size_t size)
  71. {
  72. int err;
  73. err = libusb_control_transfer(
  74. priv->m->usb_ctx->h,
  75. LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS |
  76. LIBUSB_RECIPIENT_INTERFACE,
  77. request, command, 0,
  78. (unsigned char *)buf, size,
  79. RAZER_USB_TIMEOUT);
  80. if (err < 0 || (size_t)err != size) {
  81. razer_error("razer-taipan: "
  82. "USB write 0x%02X 0x%02X failed: %d\n",
  83. request, command, err);
  84. return err;
  85. }
  86. return 0;
  87. }
  88. static int taipan_usb_read(struct taipan_private *priv,
  89. int request, int command,
  90. void *buf, size_t size)
  91. {
  92. int err;
  93. err = libusb_control_transfer(
  94. priv->m->usb_ctx->h,
  95. LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_CLASS |
  96. LIBUSB_RECIPIENT_INTERFACE,
  97. request, command, 0,
  98. buf, size,
  99. RAZER_USB_TIMEOUT);
  100. if (err < 0 || (size_t)err != size) {
  101. razer_error("razer-taipan: "
  102. "USB read 0x%02X 0x%02X failed: %d\n",
  103. request, command, err);
  104. return err;
  105. }
  106. return 0;
  107. }
  108. static int taipan_send_command(struct taipan_private *priv,
  109. struct taipan_command *cmd)
  110. {
  111. int err;
  112. cmd->checksum = razer_xor8_checksum((uint8_t *)cmd + 2, sizeof(*cmd) - 4);
  113. err = taipan_usb_write(priv, LIBUSB_REQUEST_SET_CONFIGURATION, 0x300,
  114. cmd, sizeof(*cmd));
  115. if (err)
  116. return err;
  117. err = taipan_usb_read(priv, LIBUSB_REQUEST_CLEAR_FEATURE, 0x300,
  118. cmd, sizeof(*cmd));
  119. if (err)
  120. return err;
  121. if (cmd->status != 2 &&
  122. cmd->status != 1 &&
  123. cmd->status != 0) {
  124. razer_error("razer-taipan: Command %04X/%04X failed with %02X\n",
  125. be16_to_cpu(cmd->command),
  126. be16_to_cpu(cmd->request),
  127. cmd->status);
  128. }
  129. return 0;
  130. }
  131. static int taipan_read_fw_ver(struct taipan_private *priv)
  132. {
  133. struct taipan_command cmd;
  134. uint16_t ver;
  135. int err;
  136. unsigned int i;
  137. /* Poke the device several times until it responds with a
  138. * valid version number */
  139. for (i = 0; i < 5; i++) {
  140. taipan_command_init(&cmd);
  141. cmd.command = cpu_to_be16(0x0200);
  142. cmd.request = cpu_to_be16(0x8100);
  143. err = taipan_send_command(priv, &cmd);
  144. ver = be16_to_cpu(cmd.value0);
  145. if (!err && (ver & 0xFF00) != 0)
  146. return ver;
  147. razer_msleep(100);
  148. }
  149. razer_error("razer-taipan: Failed to read firmware version\n");
  150. /* FIXME: Ignore the error and return 0 until we find out
  151. * why some mice fail to return a valid version number.
  152. */
  153. return 0;
  154. }
  155. static int taipan_do_commit(struct taipan_private *priv)
  156. {
  157. struct taipan_command cmd;
  158. unsigned int xres, yres;
  159. uint16_t freq;
  160. int err;
  161. /* Set the resolution. */
  162. taipan_command_init(&cmd);
  163. cmd.command = cpu_to_be16(0x0704);
  164. cmd.request = cpu_to_be16(0x0500);
  165. xres = (unsigned int)priv->cur_dpimapping_X->res[RAZER_DIM_0];
  166. yres = (unsigned int)priv->cur_dpimapping_Y->res[RAZER_DIM_0];
  167. cmd.value0 = cpu_to_be16(xres);
  168. cmd.value1 = cpu_to_be16(yres);
  169. err = taipan_send_command(priv, &cmd);
  170. if (err)
  171. return err;
  172. /* Set the scroll wheel and buttons LEDs. */
  173. taipan_command_init(&cmd);
  174. cmd.command = cpu_to_be16(0x0303);
  175. cmd.request = cpu_to_be16(0x0001);
  176. cmd.value0 = cpu_to_be16(0x0100);
  177. if (priv->led_states[TAIPAN_LED_SCROLL])
  178. cmd.value0 |= cpu_to_be16(0x0001);
  179. err = taipan_send_command(priv, &cmd);
  180. if (err)
  181. return err;
  182. /* Set the logo LED. */
  183. taipan_command_init(&cmd);
  184. cmd.command = cpu_to_be16(0x0303);
  185. cmd.request = cpu_to_be16(0x0001);
  186. cmd.value0 = cpu_to_be16(0x0400);
  187. if (priv->led_states[TAIPAN_LED_LOGO])
  188. cmd.value0 |= cpu_to_be16(0x0001);
  189. err = taipan_send_command(priv, &cmd);
  190. if (err)
  191. return err;
  192. /* Set scan frequency. */
  193. switch (priv->frequency) {
  194. case RAZER_MOUSE_FREQ_125HZ:
  195. freq = 0x0008;
  196. break;
  197. case RAZER_MOUSE_FREQ_500HZ:
  198. freq = 0x0002;
  199. break;
  200. case RAZER_MOUSE_FREQ_1000HZ:
  201. case RAZER_MOUSE_FREQ_UNKNOWN:
  202. freq = 0x0001;
  203. break;
  204. default:
  205. return -EINVAL;
  206. }
  207. taipan_command_init(&cmd);
  208. cmd.command = cpu_to_be16(0x0100);
  209. cmd.request = cpu_to_be16(0x0500);
  210. cmd.request |= cpu_to_be16(freq);
  211. err = taipan_send_command(priv, &cmd);
  212. if (err)
  213. return err;
  214. return 0;
  215. }
  216. static int taipan_get_fw_version(struct razer_mouse *m)
  217. {
  218. struct taipan_private *priv = m->drv_data;
  219. return priv->fw_version;
  220. }
  221. static int taipan_commit(struct razer_mouse *m, int force)
  222. {
  223. struct taipan_private *priv = m->drv_data;
  224. int err = 0;
  225. if (!m->claim_count)
  226. return -EBUSY;
  227. if (priv->commit_pending || force) {
  228. err = taipan_do_commit(priv);
  229. if (!err)
  230. priv->commit_pending = 0;
  231. }
  232. return err;
  233. }
  234. static int taipan_led_toggle(struct razer_led *led,
  235. enum razer_led_state new_state)
  236. {
  237. struct razer_mouse *m = led->u.mouse;
  238. struct taipan_private *priv = m->drv_data;
  239. if (led->id >= TAIPAN_NR_LEDS)
  240. return -EINVAL;
  241. if ((new_state != RAZER_LED_OFF) &&
  242. (new_state != RAZER_LED_ON))
  243. return -EINVAL;
  244. if (!priv->m->claim_count)
  245. return -EBUSY;
  246. priv->led_states[led->id] = new_state;
  247. priv->commit_pending = 1;
  248. return 0;
  249. }
  250. static int taipan_get_leds(struct razer_mouse *m,
  251. struct razer_led **leds_list)
  252. {
  253. struct taipan_private *priv = m->drv_data;
  254. struct razer_led *scroll, *logo;
  255. scroll = zalloc(sizeof(struct razer_led));
  256. if (!scroll)
  257. return -ENOMEM;
  258. logo = zalloc(sizeof(struct razer_led));
  259. if (!logo) {
  260. free(scroll);
  261. return -ENOMEM;
  262. }
  263. scroll->name = "Scrollwheel";
  264. scroll->id = TAIPAN_LED_SCROLL;
  265. scroll->state = priv->led_states[TAIPAN_LED_SCROLL];
  266. scroll->toggle_state = taipan_led_toggle;
  267. scroll->u.mouse = m;
  268. logo->name = "GlowingLogo";
  269. logo->id = TAIPAN_LED_LOGO;
  270. logo->state = priv->led_states[TAIPAN_LED_LOGO];
  271. logo->toggle_state = taipan_led_toggle;
  272. logo->u.mouse = m;
  273. /* Link the list */
  274. *leds_list = scroll;
  275. scroll->next = logo;
  276. logo->next = NULL;
  277. return TAIPAN_NR_LEDS;
  278. }
  279. static int taipan_supported_freqs(struct razer_mouse *m,
  280. enum razer_mouse_freq **freq_list)
  281. {
  282. enum razer_mouse_freq *list;
  283. const int count = 3;
  284. list = zalloc(sizeof(*list) * count);
  285. if (!list)
  286. return -ENOMEM;
  287. list[0] = RAZER_MOUSE_FREQ_125HZ;
  288. list[1] = RAZER_MOUSE_FREQ_500HZ;
  289. list[2] = RAZER_MOUSE_FREQ_1000HZ;
  290. *freq_list = list;
  291. return count;
  292. }
  293. static enum razer_mouse_freq taipan_get_freq(struct razer_mouse_profile *p)
  294. {
  295. struct taipan_private *priv = p->mouse->drv_data;
  296. return priv->frequency;
  297. }
  298. static int taipan_set_freq(struct razer_mouse_profile *p,
  299. enum razer_mouse_freq freq)
  300. {
  301. struct taipan_private *priv = p->mouse->drv_data;
  302. if (!priv->m->claim_count)
  303. return -EBUSY;
  304. priv->frequency = freq;
  305. priv->commit_pending = 1;
  306. return 0;
  307. }
  308. static int taipan_supported_axes(struct razer_mouse *m,
  309. struct razer_axis **axes_list)
  310. {
  311. struct taipan_private *priv = m->drv_data;
  312. *axes_list = priv->axes;
  313. return ARRAY_SIZE(priv->axes);
  314. }
  315. static int taipan_supported_resolutions(struct razer_mouse *m,
  316. enum razer_mouse_res **res_list)
  317. {
  318. enum razer_mouse_res *list;
  319. unsigned int i;
  320. const unsigned int count = TAIPAN_NR_DPIMAPPINGS;
  321. list = zalloc(sizeof(*list) * count);
  322. if (!list)
  323. return -ENOMEM;
  324. for (i = 0; i < count; i++)
  325. list[i] = (i + 1) * 100;
  326. *res_list = list;
  327. return count;
  328. }
  329. static struct razer_mouse_profile * taipan_get_profiles(struct razer_mouse *m)
  330. {
  331. struct taipan_private *priv = m->drv_data;
  332. return &priv->profile;
  333. }
  334. static int taipan_supported_dpimappings(struct razer_mouse *m,
  335. struct razer_mouse_dpimapping **res_ptr)
  336. {
  337. struct taipan_private *priv = m->drv_data;
  338. *res_ptr = &priv->dpimapping[0];
  339. return ARRAY_SIZE(priv->dpimapping);
  340. }
  341. static struct razer_mouse_dpimapping * taipan_get_dpimapping(struct razer_mouse_profile *p,
  342. struct razer_axis *axis)
  343. {
  344. struct taipan_private *priv = p->mouse->drv_data;
  345. if (!axis)
  346. axis = &priv->axes[0];
  347. if (axis->id == 0)
  348. return priv->cur_dpimapping_X;
  349. if (axis->id == 1)
  350. return priv->cur_dpimapping_Y;
  351. return NULL;
  352. }
  353. static int taipan_set_dpimapping(struct razer_mouse_profile *p,
  354. struct razer_axis *axis,
  355. struct razer_mouse_dpimapping *d)
  356. {
  357. struct taipan_private *priv = p->mouse->drv_data;
  358. if (!priv->m->claim_count)
  359. return -EBUSY;
  360. if (axis && axis->id >= ARRAY_SIZE(priv->axes))
  361. return -EINVAL;
  362. if (axis) {
  363. if (axis->id == 0)
  364. priv->cur_dpimapping_X = d;
  365. else if (axis->id == 1)
  366. priv->cur_dpimapping_Y = d;
  367. else
  368. return -EINVAL;
  369. } else {
  370. priv->cur_dpimapping_X = d;
  371. priv->cur_dpimapping_Y = d;
  372. }
  373. priv->commit_pending = 1;
  374. return 0;
  375. }
  376. int razer_taipan_init(struct razer_mouse *m,
  377. struct libusb_device *usbdev)
  378. {
  379. struct taipan_private *priv;
  380. unsigned int i;
  381. int fwver, err;
  382. BUILD_BUG_ON(sizeof(struct taipan_command) != 90);
  383. priv = zalloc(sizeof(struct taipan_private));
  384. if (!priv)
  385. return -ENOMEM;
  386. priv->m = m;
  387. m->drv_data = priv;
  388. err = razer_usb_add_used_interface(m->usb_ctx, 0, 0);
  389. if (err)
  390. goto err_free;
  391. err = m->claim(m);
  392. if (err) {
  393. razer_error("hw_taipan: Failed to claim device\n");
  394. goto err_free;
  395. }
  396. /* Fetch firmware version */
  397. fwver = taipan_read_fw_ver(priv);
  398. if (fwver < 0) {
  399. err = fwver;
  400. goto err_release;
  401. }
  402. priv->fw_version = fwver;
  403. priv->frequency = RAZER_MOUSE_FREQ_1000HZ;
  404. for (i = 0; i < TAIPAN_NR_LEDS; i++)
  405. priv->led_states[i] = RAZER_LED_ON;
  406. priv->profile.nr = 0;
  407. priv->profile.get_freq = taipan_get_freq;
  408. priv->profile.set_freq = taipan_set_freq;
  409. priv->profile.get_dpimapping = taipan_get_dpimapping;
  410. priv->profile.set_dpimapping = taipan_set_dpimapping;
  411. priv->profile.mouse = m;
  412. for (i = 0; i < TAIPAN_NR_DPIMAPPINGS; i++) {
  413. priv->dpimapping[i].nr = i;
  414. priv->dpimapping[i].res[RAZER_DIM_0] = (i + 1) * 100;
  415. if (priv->dpimapping[i].res[RAZER_DIM_0] == 1000) {
  416. priv->cur_dpimapping_X = &priv->dpimapping[i];
  417. priv->cur_dpimapping_Y = &priv->dpimapping[i];
  418. }
  419. priv->dpimapping[i].dimension_mask = (1 << RAZER_DIM_0);
  420. priv->dpimapping[i].change = NULL;
  421. priv->dpimapping[i].mouse = m;
  422. }
  423. razer_init_axes(&priv->axes[0],
  424. "X", RAZER_AXIS_INDEPENDENT_DPIMAPPING,
  425. "Y", RAZER_AXIS_INDEPENDENT_DPIMAPPING,
  426. "Scroll", 0);
  427. m->type = RAZER_MOUSETYPE_TAIPAN;
  428. razer_generic_usb_gen_idstr(usbdev, m->usb_ctx->h, "Taipan", 1,
  429. NULL, m->idstr);
  430. m->get_fw_version = taipan_get_fw_version;
  431. m->commit = taipan_commit;
  432. m->global_get_leds = taipan_get_leds;
  433. m->get_profiles = taipan_get_profiles;
  434. m->supported_axes = taipan_supported_axes;
  435. m->supported_resolutions = taipan_supported_resolutions;
  436. m->supported_freqs = taipan_supported_freqs;
  437. m->supported_dpimappings = taipan_supported_dpimappings;
  438. err = taipan_do_commit(priv);
  439. if (err) {
  440. razer_error("hw_taipan: Failed to commit initial settings\n");
  441. goto err_release;
  442. }
  443. m->release(m);
  444. return 0;
  445. err_release:
  446. m->release(m);
  447. err_free:
  448. free(priv);
  449. return err;
  450. }
  451. void razer_taipan_release(struct razer_mouse *m)
  452. {
  453. struct taipan_private *priv = m->drv_data;
  454. free(priv);
  455. }