hw_deathadder_chroma.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857
  1. /*
  2. * Lowlevel hardware access for the Razer DeathAdder Chroma mouse.
  3. *
  4. * Important notice:
  5. * This hardware driver is based on reverse engineering, only.
  6. *
  7. * Copyright (C) 2015 Konrad Zemek <konrad.zemek@gmail.com>
  8. *
  9. * This program is free software; you can redistribute it and/or modify it under
  10. * the terms of the GNU General Public License as published by the Free Software
  11. * Foundation; either version 2 of the License, or (at your option) any later
  12. * version.
  13. *
  14. * This program is distributed in the hope that it will be useful, but WITHOUT
  15. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  16. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  17. * details.
  18. */
  19. #include "hw_deathadder_chroma.h"
  20. #include "razer_private.h"
  21. #include <errno.h>
  22. #include <stdlib.h>
  23. #include <stdio.h>
  24. #include <stdint.h>
  25. #include <string.h>
  26. static enum razer_mouse_freq deathadder_chroma_freqs_list[] = {
  27. RAZER_MOUSE_FREQ_125HZ, RAZER_MOUSE_FREQ_500HZ, RAZER_MOUSE_FREQ_1000HZ};
  28. static enum razer_mouse_res deathadder_chroma_resolution_stages_list[] = {
  29. RAZER_MOUSE_RES_800DPI, RAZER_MOUSE_RES_1800DPI, RAZER_MOUSE_RES_3500DPI,
  30. RAZER_MOUSE_RES_5600DPI, RAZER_MOUSE_RES_10000DPI};
  31. #define DEATHADDER_CHROMA_DEVICE_NAME "DeathAdder Chroma"
  32. #define DEATHADDER_CHROMA_SCROLL_NAME "Scrollwheel"
  33. #define DEATHADDER_CHROMA_LOGO_NAME "GlowingLogo"
  34. enum deathadder_chroma_led_id {
  35. DEATHADDER_CHROMA_LED_ID_SCROLL = 0x01,
  36. DEATHADDER_CHROMA_LED_ID_LOGO = 0x04
  37. };
  38. enum deathadder_chroma_led_mode {
  39. DEATHADDER_CHROMA_LED_MODE_STATIC = 0x00,
  40. DEATHADDER_CHROMA_LED_MODE_BREATHING = 0x02,
  41. DEATHADDER_CHROMA_LED_MODE_SPECTRUM = 0X04
  42. };
  43. enum deathadder_chroma_led_state {
  44. DEATHADDER_CHROMA_LED_STATE_OFF = 0x00,
  45. DEATHADDER_CHROMA_LED_STATE_ON = 0x01
  46. };
  47. /*
  48. * The 6th byte of DeathAdder Chroma's command seems to be the size of arguments
  49. * (size of arguments to read in case of read operations). It's not necessarily
  50. * so, since some values are slightly off (i.e. bigger than the apparent size
  51. * of the arguments).
  52. * Experiments suggest that the value given in the 'size' byte does not matter.
  53. * I chose to go with the values used by the Synapse driver.
  54. */
  55. enum deathadder_chroma_request_size {
  56. DEATHADDER_CHROMA_REQUEST_SIZE_INIT = 0x02,
  57. DEATHADDER_CHROMA_REQUEST_SIZE_SET_RESOLUTION = 0x07,
  58. DEATHADDER_CHROMA_REQUEST_SIZE_GET_FIRMWARE = 0x04,
  59. DEATHADDER_CHROMA_REQUEST_SIZE_GET_SERIAL_NO = 0x16,
  60. DEATHADDER_CHROMA_REQUEST_SIZE_SET_FREQUENCY = 0x01,
  61. DEATHADDER_CHROMA_REQUEST_SIZE_SET_LED_STATE = 0x03,
  62. DEATHADDER_CHROMA_REQUEST_SIZE_SET_LED_MODE = 0x03,
  63. DEATHADDER_CHROMA_REQUEST_SIZE_SET_LED_COLOR = 0x05
  64. };
  65. enum deathadder_chroma_request {
  66. DEATHADDER_CHROMA_REQUEST_INIT = 0x0004,
  67. DEATHADDER_CHROMA_REQUEST_SET_RESOLUTION = 0x0405,
  68. DEATHADDER_CHROMA_REQUEST_GET_FIRMWARE = 0x0087,
  69. DEATHADDER_CHROMA_REQUEST_GET_SERIAL_NO = 0x0082,
  70. DEATHADDER_CHROMA_REQUEST_SET_FREQUENCY = 0x0005,
  71. DEATHADDER_CHROMA_REQUEST_SET_LED_STATE = 0x0300,
  72. DEATHADDER_CHROMA_REQUEST_SET_LED_MODE = 0x0302,
  73. DEATHADDER_CHROMA_REQUEST_SET_LED_COLOR = 0x0301
  74. };
  75. enum deathadder_chroma_constants {
  76. DEATHADDER_CHROMA_MAX_FREQUENCY = RAZER_MOUSE_FREQ_1000HZ,
  77. DEATHADDER_CHROMA_MAX_RESOLUTION = RAZER_MOUSE_RES_10000DPI,
  78. DEATHADDER_CHROMA_RESOLUTION_STEP = RAZER_MOUSE_RES_100DPI,
  79. DEATHADDER_CHROMA_LED_NUM = 2,
  80. DEATHADDER_CHROMA_AXES_NUM = 2,
  81. DEATHADDER_CHROMA_SUPPORTED_FREQ_NUM =
  82. ARRAY_SIZE(deathadder_chroma_freqs_list),
  83. DEATHADDER_CHROMA_DPIMAPPINGS_NUM =
  84. ARRAY_SIZE(deathadder_chroma_resolution_stages_list),
  85. DEATHADDER_CHROMA_USB_SETUP_PACKET_VALUE = 0x300,
  86. DEATHADDER_CHROMA_SUCCESS_STATUS = 0x02,
  87. DEATHADDER_CHROMA_PACKET_SPACING_MS = 35,
  88. /*
  89. * Experiments suggest that the value in the 'magic' byte of the command
  90. * does not necessarily matter (e.g. the commands work when the 'magic'
  91. * byte equals 0x77). I chose to go with the value used by the Synapse
  92. * driver.
  93. */
  94. DEATHADDER_CHROMA_MAGIC_BYTE = 0xFF,
  95. /*
  96. * These specific arg0 bytes are used by the Synapse driver for their
  97. * respective commands. Their value may or may not matter (e.g. matters
  98. * in the case of LED commands, doesn't seem to matter for the
  99. * resolution command).
  100. */
  101. DEATHADDER_CHROMA_LED_ARG0 = 0x01,
  102. DEATHADDER_CHROMA_INIT_ARG0 = 0x03,
  103. DEATHADDER_CHROMA_RESOLUTION_ARG0 = 0x00
  104. };
  105. struct deathadder_chroma_command
  106. {
  107. uint8_t status;
  108. uint8_t magic;
  109. uint8_t padding0[3];
  110. uint8_t size;
  111. be16_t request;
  112. union
  113. {
  114. uint8_t bvalue[80];
  115. struct
  116. {
  117. uint8_t padding1;
  118. be16_t value[38];
  119. uint8_t padding2;
  120. } _packed;
  121. } _packed;
  122. uint8_t checksum;
  123. uint8_t padding3;
  124. } _packed;
  125. #define DEATHADDER_CHROMA_COMMAND_INIT \
  126. (struct deathadder_chroma_command) \
  127. { \
  128. .magic = DEATHADDER_CHROMA_MAGIC_BYTE \
  129. }
  130. struct deathadder_chroma_rgb_color
  131. {
  132. uint8_t r;
  133. uint8_t g;
  134. uint8_t b;
  135. };
  136. struct deathadder_chroma_led
  137. {
  138. enum deathadder_chroma_led_id id;
  139. enum deathadder_chroma_led_mode mode;
  140. enum deathadder_chroma_led_state state;
  141. struct deathadder_chroma_rgb_color color;
  142. };
  143. struct deathadder_chroma_driver_data
  144. {
  145. struct razer_event_spacing packet_spacing;
  146. struct razer_mouse_profile profile;
  147. struct razer_mouse_dpimapping *current_dpimapping;
  148. enum razer_mouse_freq current_freq;
  149. struct deathadder_chroma_led scroll_led;
  150. struct deathadder_chroma_led logo_led;
  151. struct razer_mouse_dpimapping
  152. dpimappings[DEATHADDER_CHROMA_DPIMAPPINGS_NUM];
  153. struct razer_axis axes[DEATHADDER_CHROMA_AXES_NUM];
  154. uint16_t fw_version;
  155. char serial[DEATHADDER_CHROMA_REQUEST_SIZE_GET_SERIAL_NO + 1];
  156. };
  157. static uint8_t deathadder_chroma_checksum(struct deathadder_chroma_command *cmd)
  158. {
  159. size_t control_size;
  160. control_size = sizeof(cmd->size) + sizeof(cmd->request);
  161. return razer_xor8_checksum((uint8_t *)&cmd->size,
  162. control_size + cmd->size);
  163. }
  164. static int deathadder_chroma_translate_frequency(enum razer_mouse_freq freq)
  165. {
  166. switch (freq) {
  167. case RAZER_MOUSE_FREQ_UNKNOWN:
  168. freq = RAZER_MOUSE_FREQ_500HZ;
  169. /* fall through */
  170. case RAZER_MOUSE_FREQ_125HZ:
  171. case RAZER_MOUSE_FREQ_500HZ:
  172. case RAZER_MOUSE_FREQ_1000HZ:
  173. return DEATHADDER_CHROMA_MAX_FREQUENCY / freq;
  174. default:
  175. return -EINVAL;
  176. }
  177. }
  178. static int deathadder_chroma_usb_action(
  179. struct razer_mouse *m, enum libusb_endpoint_direction direction,
  180. enum libusb_standard_request request, uint16_t command,
  181. struct deathadder_chroma_command *cmd)
  182. {
  183. int err;
  184. struct deathadder_chroma_driver_data *drv_data;
  185. drv_data = m->drv_data;
  186. razer_event_spacing_enter(&drv_data->packet_spacing);
  187. err = libusb_control_transfer(m->usb_ctx->h,
  188. direction | LIBUSB_REQUEST_TYPE_CLASS |
  189. LIBUSB_RECIPIENT_INTERFACE,
  190. request, command, 0, (unsigned char *)cmd,
  191. sizeof(*cmd), RAZER_USB_TIMEOUT);
  192. razer_event_spacing_leave(&drv_data->packet_spacing);
  193. if (err != sizeof(*cmd)) {
  194. razer_error("razer-deathadder-chroma: "
  195. "USB %s 0x%01X 0x%02X failed with %d\n",
  196. direction == LIBUSB_ENDPOINT_IN ? "read" : "write",
  197. request, command, err);
  198. return err;
  199. }
  200. return 0;
  201. }
  202. static int deathadder_chroma_send_command(struct razer_mouse *m,
  203. struct deathadder_chroma_command *cmd)
  204. {
  205. int err;
  206. uint8_t checksum;
  207. cmd->checksum = deathadder_chroma_checksum(cmd);
  208. err = deathadder_chroma_usb_action(
  209. m, LIBUSB_ENDPOINT_OUT, LIBUSB_REQUEST_SET_CONFIGURATION,
  210. DEATHADDER_CHROMA_USB_SETUP_PACKET_VALUE, cmd);
  211. if (err)
  212. return err;
  213. err = deathadder_chroma_usb_action(
  214. m, LIBUSB_ENDPOINT_IN, LIBUSB_REQUEST_CLEAR_FEATURE,
  215. DEATHADDER_CHROMA_USB_SETUP_PACKET_VALUE, cmd);
  216. if (err)
  217. return err;
  218. checksum = deathadder_chroma_checksum(cmd);
  219. if (checksum != cmd->checksum) {
  220. razer_error("razer-deathadder-chroma: "
  221. "Command %02X %04X bad response checksum %02X "
  222. "(expected %02X)\n",
  223. cmd->size, be16_to_cpu(cmd->request), checksum,
  224. cmd->checksum);
  225. return -EBADMSG;
  226. }
  227. if (cmd->status != DEATHADDER_CHROMA_SUCCESS_STATUS)
  228. razer_error("razer-deathadder-chroma: "
  229. "Command %02X %04X failed with %02X\n",
  230. cmd->size, be16_to_cpu(cmd->request), cmd->status);
  231. return 0;
  232. }
  233. static int deathadder_chroma_send_init_command(struct razer_mouse *m)
  234. {
  235. struct deathadder_chroma_command cmd;
  236. cmd = DEATHADDER_CHROMA_COMMAND_INIT;
  237. cmd.size = DEATHADDER_CHROMA_REQUEST_SIZE_INIT;
  238. cmd.request = cpu_to_be16(DEATHADDER_CHROMA_REQUEST_INIT);
  239. cmd.bvalue[0] = DEATHADDER_CHROMA_INIT_ARG0;
  240. return deathadder_chroma_send_command(m, &cmd);
  241. }
  242. static int deathadder_chroma_send_set_resolution_command(struct razer_mouse *m)
  243. {
  244. enum razer_mouse_res res_x, res_y;
  245. struct deathadder_chroma_command cmd;
  246. struct deathadder_chroma_driver_data *drv_data;
  247. drv_data = m->drv_data;
  248. res_x = drv_data->current_dpimapping->res[RAZER_DIM_X];
  249. res_y = drv_data->current_dpimapping->res[RAZER_DIM_Y];
  250. cmd = DEATHADDER_CHROMA_COMMAND_INIT;
  251. cmd.size = DEATHADDER_CHROMA_REQUEST_SIZE_SET_RESOLUTION;
  252. cmd.request = cpu_to_be16(DEATHADDER_CHROMA_REQUEST_SET_RESOLUTION);
  253. cmd.bvalue[0] = DEATHADDER_CHROMA_RESOLUTION_ARG0;
  254. cmd.value[0] = cpu_to_be16(res_x);
  255. cmd.value[1] = cpu_to_be16(res_y);
  256. return deathadder_chroma_send_command(m, &cmd);
  257. }
  258. static int deathadder_chroma_send_get_firmware_command(struct razer_mouse *m)
  259. {
  260. int err;
  261. uint8_t fw_major;
  262. uint16_t fw_minor;
  263. struct deathadder_chroma_command cmd;
  264. struct deathadder_chroma_driver_data *drv_data;
  265. drv_data = m->drv_data;
  266. cmd = DEATHADDER_CHROMA_COMMAND_INIT;
  267. cmd.size = DEATHADDER_CHROMA_REQUEST_SIZE_GET_FIRMWARE;
  268. cmd.request = cpu_to_be16(DEATHADDER_CHROMA_REQUEST_GET_FIRMWARE);
  269. err = deathadder_chroma_send_command(m, &cmd);
  270. if (err)
  271. return err;
  272. fw_major = cmd.bvalue[0];
  273. fw_minor = be16_to_cpu(cmd.value[0]);
  274. drv_data->fw_version = (fw_major << 8) | fw_minor;
  275. return 0;
  276. }
  277. static int deathadder_chroma_send_get_serial_no_command(struct razer_mouse *m)
  278. {
  279. int err;
  280. struct deathadder_chroma_command cmd;
  281. struct deathadder_chroma_driver_data *drv_data;
  282. drv_data = m->drv_data;
  283. cmd = DEATHADDER_CHROMA_COMMAND_INIT;
  284. cmd.size = DEATHADDER_CHROMA_REQUEST_SIZE_GET_SERIAL_NO;
  285. cmd.request = cpu_to_be16(DEATHADDER_CHROMA_REQUEST_GET_SERIAL_NO);
  286. err = deathadder_chroma_send_command(m, &cmd);
  287. if (err)
  288. return err;
  289. strncpy(drv_data->serial, (const char *)cmd.bvalue,
  290. DEATHADDER_CHROMA_REQUEST_SIZE_GET_SERIAL_NO);
  291. drv_data->serial[DEATHADDER_CHROMA_REQUEST_SIZE_GET_SERIAL_NO] = '\0';
  292. return 0;
  293. }
  294. static int deathadder_chroma_send_set_frequency_command(struct razer_mouse *m)
  295. {
  296. int tfreq;
  297. struct deathadder_chroma_command cmd;
  298. struct deathadder_chroma_driver_data *drv_data;
  299. drv_data = m->drv_data;
  300. cmd = DEATHADDER_CHROMA_COMMAND_INIT;
  301. tfreq = deathadder_chroma_translate_frequency(drv_data->current_freq);
  302. if (tfreq < 0)
  303. return tfreq;
  304. cmd.size = DEATHADDER_CHROMA_REQUEST_SIZE_SET_FREQUENCY;
  305. cmd.request = cpu_to_be16(DEATHADDER_CHROMA_REQUEST_SET_FREQUENCY);
  306. cmd.bvalue[0] = tfreq;
  307. return deathadder_chroma_send_command(m, &cmd);
  308. }
  309. static struct deathadder_chroma_led *
  310. deathadder_chroma_get_led(struct deathadder_chroma_driver_data *d,
  311. enum deathadder_chroma_led_id led_id)
  312. {
  313. switch (led_id) {
  314. case DEATHADDER_CHROMA_LED_ID_LOGO:
  315. return &d->logo_led;
  316. case DEATHADDER_CHROMA_LED_ID_SCROLL:
  317. return &d->scroll_led;
  318. default:
  319. return NULL;
  320. }
  321. }
  322. static int
  323. deathadder_chroma_send_set_led_state_command(struct razer_mouse *m,
  324. struct deathadder_chroma_led *led)
  325. {
  326. struct deathadder_chroma_command cmd;
  327. cmd = DEATHADDER_CHROMA_COMMAND_INIT;
  328. cmd.size = DEATHADDER_CHROMA_REQUEST_SIZE_SET_LED_STATE;
  329. cmd.request = cpu_to_be16(DEATHADDER_CHROMA_REQUEST_SET_LED_STATE);
  330. cmd.bvalue[0] = DEATHADDER_CHROMA_LED_ARG0;
  331. cmd.bvalue[1] = led->id;
  332. cmd.bvalue[2] = led->state;
  333. return deathadder_chroma_send_command(m, &cmd);
  334. }
  335. static int
  336. deathadder_chroma_send_set_led_mode_command(struct razer_mouse *m,
  337. struct deathadder_chroma_led *led)
  338. {
  339. struct deathadder_chroma_command cmd;
  340. cmd = DEATHADDER_CHROMA_COMMAND_INIT;
  341. cmd.size = DEATHADDER_CHROMA_REQUEST_SIZE_SET_LED_MODE;
  342. cmd.request = cpu_to_be16(DEATHADDER_CHROMA_REQUEST_SET_LED_MODE);
  343. cmd.bvalue[0] = DEATHADDER_CHROMA_LED_ARG0;
  344. cmd.bvalue[1] = led->id;
  345. cmd.bvalue[2] = led->mode;
  346. return deathadder_chroma_send_command(m, &cmd);
  347. }
  348. static int
  349. deathadder_chroma_send_set_led_color_command(struct razer_mouse *m,
  350. struct deathadder_chroma_led *led)
  351. {
  352. struct deathadder_chroma_command cmd;
  353. cmd = DEATHADDER_CHROMA_COMMAND_INIT;
  354. cmd.size = DEATHADDER_CHROMA_REQUEST_SIZE_SET_LED_COLOR;
  355. cmd.request = cpu_to_be16(DEATHADDER_CHROMA_REQUEST_SET_LED_COLOR);
  356. cmd.bvalue[0] = DEATHADDER_CHROMA_LED_ARG0;
  357. cmd.bvalue[1] = led->id;
  358. cmd.bvalue[2] = led->color.r;
  359. cmd.bvalue[3] = led->color.g;
  360. cmd.bvalue[4] = led->color.b;
  361. return deathadder_chroma_send_command(m, &cmd);
  362. }
  363. static int deathadder_chroma_get_fw_version(struct razer_mouse *m)
  364. {
  365. struct deathadder_chroma_driver_data *drv_data;
  366. drv_data = m->drv_data;
  367. return drv_data->fw_version;
  368. }
  369. static struct razer_mouse_profile *
  370. deathadder_chroma_get_profiles(struct razer_mouse *m)
  371. {
  372. struct deathadder_chroma_driver_data *drv_data;
  373. drv_data = m->drv_data;
  374. return &drv_data->profile;
  375. }
  376. static int deathadder_chroma_supported_axes(struct razer_mouse *m,
  377. struct razer_axis **res_ptr)
  378. {
  379. struct deathadder_chroma_driver_data *drv_data;
  380. drv_data = m->drv_data;
  381. *res_ptr = drv_data->axes;
  382. return ARRAY_SIZE(drv_data->axes);
  383. }
  384. static int
  385. deathadder_chroma_supported_dpimappings(struct razer_mouse *m,
  386. struct razer_mouse_dpimapping **res_ptr)
  387. {
  388. struct deathadder_chroma_driver_data *drv_data;
  389. drv_data = m->drv_data;
  390. *res_ptr = drv_data->dpimappings;
  391. return ARRAY_SIZE(drv_data->dpimappings);
  392. }
  393. static int
  394. deathadder_chroma_supported_resolutions(struct razer_mouse *m,
  395. enum razer_mouse_res **res_ptr)
  396. {
  397. size_t i;
  398. size_t step_number;
  399. step_number = DEATHADDER_CHROMA_MAX_RESOLUTION /
  400. DEATHADDER_CHROMA_RESOLUTION_STEP;
  401. *res_ptr = calloc(step_number, sizeof(enum razer_mouse_res));
  402. if (!*res_ptr)
  403. return -ENOMEM;
  404. for (i = 0; i < step_number; ++i)
  405. (*res_ptr)[i] = (i + 1) * DEATHADDER_CHROMA_RESOLUTION_STEP;
  406. return step_number;
  407. }
  408. static int deathadder_chroma_supported_freqs(struct razer_mouse *m,
  409. enum razer_mouse_freq **res_ptr)
  410. {
  411. *res_ptr = malloc(sizeof(deathadder_chroma_freqs_list));
  412. if (!*res_ptr)
  413. return -ENOMEM;
  414. memcpy(*res_ptr, deathadder_chroma_freqs_list,
  415. sizeof(deathadder_chroma_freqs_list));
  416. return DEATHADDER_CHROMA_SUPPORTED_FREQ_NUM;
  417. }
  418. static enum razer_mouse_freq
  419. deathadder_chroma_get_freq(struct razer_mouse_profile *p)
  420. {
  421. struct deathadder_chroma_driver_data *drv_data;
  422. drv_data = p->mouse->drv_data;
  423. return drv_data->current_freq;
  424. }
  425. static struct razer_mouse_dpimapping *
  426. deathadder_chroma_get_dpimapping(struct razer_mouse_profile *p,
  427. struct razer_axis *axis)
  428. {
  429. struct deathadder_chroma_driver_data *drv_data;
  430. drv_data = p->mouse->drv_data;
  431. return drv_data->current_dpimapping;
  432. }
  433. static int deathadder_chroma_change_dpimapping(struct razer_mouse_dpimapping *d,
  434. enum razer_dimension dim,
  435. enum razer_mouse_res res)
  436. {
  437. struct deathadder_chroma_driver_data *drv_data;
  438. if (!(d->dimension_mask & (1 << dim)))
  439. return -EINVAL;
  440. if (res == RAZER_MOUSE_RES_UNKNOWN)
  441. res = RAZER_MOUSE_RES_1800DPI;
  442. if (res < RAZER_MOUSE_RES_100DPI || res > RAZER_MOUSE_RES_10000DPI)
  443. return -EINVAL;
  444. d->res[dim] = res;
  445. drv_data = d->mouse->drv_data;
  446. if (d == drv_data->current_dpimapping)
  447. return deathadder_chroma_send_set_resolution_command(d->mouse);
  448. return 0;
  449. }
  450. static int deathadder_chroma_led_toggle_state(struct razer_led *led,
  451. enum razer_led_state new_state)
  452. {
  453. struct deathadder_chroma_driver_data *drv_data;
  454. struct deathadder_chroma_led *priv_led;
  455. drv_data = led->u.mouse->drv_data;
  456. priv_led = deathadder_chroma_get_led(drv_data, led->id);
  457. if (!priv_led)
  458. return -EINVAL;
  459. switch (new_state) {
  460. case RAZER_LED_UNKNOWN:
  461. case RAZER_LED_ON:
  462. priv_led->state = DEATHADDER_CHROMA_LED_STATE_ON;
  463. break;
  464. case RAZER_LED_OFF:
  465. priv_led->state = DEATHADDER_CHROMA_LED_STATE_OFF;
  466. break;
  467. }
  468. return deathadder_chroma_send_set_led_state_command(led->u.mouse,
  469. priv_led);
  470. }
  471. static int
  472. deathadder_chroma_led_change_color(struct razer_led *led,
  473. const struct razer_rgb_color *new_color)
  474. {
  475. struct deathadder_chroma_driver_data *drv_data;
  476. struct deathadder_chroma_led *priv_led;
  477. drv_data = led->u.mouse->drv_data;
  478. priv_led = deathadder_chroma_get_led(drv_data, led->id);
  479. if (!priv_led)
  480. return -EINVAL;
  481. if (priv_led->mode == DEATHADDER_CHROMA_LED_MODE_SPECTRUM)
  482. return -EINVAL;
  483. priv_led->color = (struct deathadder_chroma_rgb_color){
  484. .r = new_color->r, .g = new_color->g, .b = new_color->b};
  485. return deathadder_chroma_send_set_led_color_command(led->u.mouse,
  486. priv_led);
  487. }
  488. static int deathadder_chroma_set_freq(struct razer_mouse_profile *p,
  489. enum razer_mouse_freq freq)
  490. {
  491. struct deathadder_chroma_driver_data *drv_data;
  492. if (freq == RAZER_MOUSE_FREQ_UNKNOWN)
  493. freq = RAZER_MOUSE_FREQ_500HZ;
  494. if (freq != RAZER_MOUSE_FREQ_125HZ && freq != RAZER_MOUSE_FREQ_500HZ &&
  495. freq != RAZER_MOUSE_FREQ_1000HZ)
  496. return -EINVAL;
  497. drv_data = p->mouse->drv_data;
  498. drv_data->current_freq = freq;
  499. return deathadder_chroma_send_set_frequency_command(p->mouse);
  500. }
  501. static int deathadder_chroma_set_dpimapping(struct razer_mouse_profile *p,
  502. struct razer_axis *axis,
  503. struct razer_mouse_dpimapping *d)
  504. {
  505. struct deathadder_chroma_driver_data *drv_data;
  506. if (axis && axis->id > 0)
  507. return -EINVAL;
  508. drv_data = p->mouse->drv_data;
  509. drv_data->current_dpimapping = &drv_data->dpimappings[d->nr];
  510. return deathadder_chroma_send_set_resolution_command(p->mouse);
  511. }
  512. static int
  513. deathadder_chroma_translate_led_mode(enum deathadder_chroma_led_mode mode)
  514. {
  515. switch (mode) {
  516. case DEATHADDER_CHROMA_LED_MODE_STATIC:
  517. return RAZER_LED_MODE_STATIC;
  518. case DEATHADDER_CHROMA_LED_MODE_BREATHING:
  519. return RAZER_LED_MODE_BREATHING;
  520. case DEATHADDER_CHROMA_LED_MODE_SPECTRUM:
  521. return RAZER_LED_MODE_SPECTRUM;
  522. default:
  523. return -EINVAL;
  524. }
  525. }
  526. static int deathadder_chroma_translate_razer_led_mode(enum razer_led_mode mode)
  527. {
  528. switch (mode) {
  529. case RAZER_LED_MODE_STATIC:
  530. return DEATHADDER_CHROMA_LED_MODE_STATIC;
  531. case RAZER_LED_MODE_BREATHING:
  532. return DEATHADDER_CHROMA_LED_MODE_BREATHING;
  533. case RAZER_LED_MODE_SPECTRUM:
  534. return DEATHADDER_CHROMA_LED_MODE_SPECTRUM;
  535. default:
  536. return -EINVAL;
  537. }
  538. }
  539. static int deathadder_chroma_led_set_mode(struct razer_led *led,
  540. enum razer_led_mode new_mode)
  541. {
  542. int err;
  543. struct deathadder_chroma_driver_data *drv_data;
  544. struct deathadder_chroma_led *priv_led;
  545. drv_data = led->u.mouse->drv_data;
  546. priv_led = deathadder_chroma_get_led(drv_data, led->id);
  547. if (!priv_led)
  548. return -EINVAL;
  549. err = deathadder_chroma_translate_razer_led_mode(new_mode);
  550. if (err < 0)
  551. return err;
  552. priv_led->mode = err;
  553. return deathadder_chroma_send_set_led_mode_command(led->u.mouse,
  554. priv_led);
  555. }
  556. static int deathadder_chroma_get_leds(struct razer_mouse *m,
  557. struct razer_led **leds_list)
  558. {
  559. unsigned int supported_modes;
  560. enum razer_led_state scroll_state, logo_state;
  561. struct deathadder_chroma_driver_data *drv_data;
  562. struct razer_led *scroll, *logo;
  563. drv_data = m->drv_data;
  564. scroll = zalloc(sizeof(struct razer_led));
  565. if (!scroll)
  566. return -ENOMEM;
  567. logo = zalloc(sizeof(struct razer_led));
  568. if (!logo) {
  569. free(scroll);
  570. return -ENOMEM;
  571. }
  572. supported_modes = (1 << RAZER_LED_MODE_BREATHING) |
  573. (1 << RAZER_LED_MODE_SPECTRUM) |
  574. (1 << RAZER_LED_MODE_STATIC);
  575. scroll_state =
  576. drv_data->scroll_led.state == DEATHADDER_CHROMA_LED_STATE_OFF
  577. ? RAZER_LED_OFF
  578. : RAZER_LED_ON;
  579. *scroll = (struct razer_led){
  580. .id = drv_data->scroll_led.id,
  581. .name = DEATHADDER_CHROMA_SCROLL_NAME,
  582. .state = scroll_state,
  583. .u.mouse = m,
  584. .toggle_state = deathadder_chroma_led_toggle_state,
  585. .change_color = deathadder_chroma_led_change_color,
  586. .set_mode = deathadder_chroma_led_set_mode,
  587. .next = logo,
  588. .color = {.r = drv_data->scroll_led.color.r,
  589. .g = drv_data->scroll_led.color.g,
  590. .b = drv_data->scroll_led.color.b,
  591. .valid = 1},
  592. .supported_modes_mask = supported_modes,
  593. .mode = deathadder_chroma_translate_led_mode(
  594. drv_data->scroll_led.mode)};
  595. logo_state = drv_data->logo_led.state == DEATHADDER_CHROMA_LED_STATE_OFF
  596. ? RAZER_LED_OFF
  597. : RAZER_LED_ON;
  598. *logo = (struct razer_led){
  599. .id = drv_data->logo_led.id,
  600. .name = DEATHADDER_CHROMA_LOGO_NAME,
  601. .state = logo_state,
  602. .u.mouse = m,
  603. .toggle_state = deathadder_chroma_led_toggle_state,
  604. .change_color = deathadder_chroma_led_change_color,
  605. .set_mode = deathadder_chroma_led_set_mode,
  606. .color = {.r = drv_data->logo_led.color.r,
  607. .g = drv_data->logo_led.color.g,
  608. .b = drv_data->logo_led.color.b,
  609. .valid = 1},
  610. .supported_modes_mask = supported_modes,
  611. .mode =
  612. deathadder_chroma_translate_led_mode(drv_data->logo_led.mode)};
  613. *leds_list = scroll;
  614. return DEATHADDER_CHROMA_LED_NUM;
  615. }
  616. int razer_deathadder_chroma_init(struct razer_mouse *m,
  617. struct libusb_device *usbdev)
  618. {
  619. int err;
  620. size_t i;
  621. struct deathadder_chroma_driver_data *drv_data;
  622. struct deathadder_chroma_led *scroll_led, *logo_led;
  623. BUILD_BUG_ON(sizeof(struct deathadder_chroma_command) != 90);
  624. drv_data = zalloc(sizeof(*drv_data));
  625. if (!drv_data)
  626. return -ENOMEM;
  627. razer_event_spacing_init(&drv_data->packet_spacing,
  628. DEATHADDER_CHROMA_PACKET_SPACING_MS);
  629. for (i = 0; i < DEATHADDER_CHROMA_DPIMAPPINGS_NUM; ++i) {
  630. drv_data->dpimappings[i] = (struct razer_mouse_dpimapping){
  631. .nr = i,
  632. .change = deathadder_chroma_change_dpimapping,
  633. .dimension_mask = (1 << RAZER_DIM_X) | (1 << RAZER_DIM_Y),
  634. .mouse = m};
  635. drv_data->dpimappings[i].res[RAZER_DIM_X] =
  636. drv_data->dpimappings[i].res[RAZER_DIM_Y] =
  637. deathadder_chroma_resolution_stages_list[i];
  638. }
  639. drv_data->current_dpimapping = &drv_data->dpimappings[1];
  640. drv_data->current_freq = RAZER_MOUSE_FREQ_500HZ;
  641. drv_data->scroll_led = (struct deathadder_chroma_led){
  642. .id = DEATHADDER_CHROMA_LED_ID_SCROLL,
  643. .mode = DEATHADDER_CHROMA_LED_MODE_SPECTRUM,
  644. .state = DEATHADDER_CHROMA_LED_STATE_ON,
  645. .color = {0x00, 0xFF, 0x00}};
  646. drv_data->logo_led = (struct deathadder_chroma_led){
  647. .id = DEATHADDER_CHROMA_LED_ID_LOGO,
  648. .mode = DEATHADDER_CHROMA_LED_MODE_SPECTRUM,
  649. .state = DEATHADDER_CHROMA_LED_STATE_ON,
  650. .color = {0x00, 0xFF, 0x00}};
  651. razer_init_axes(drv_data->axes, "X/Y",
  652. RAZER_AXIS_INDEPENDENT_DPIMAPPING, "Scroll", 0, NULL,
  653. 0);
  654. m->drv_data = drv_data;
  655. if ((err = razer_usb_add_used_interface(m->usb_ctx, 0, 0)) ||
  656. (err = m->claim(m))) {
  657. free(drv_data);
  658. return err;
  659. }
  660. scroll_led = &drv_data->scroll_led;
  661. logo_led = &drv_data->logo_led;
  662. if ((err = deathadder_chroma_send_init_command(m)) ||
  663. (err = deathadder_chroma_send_set_resolution_command(m)) ||
  664. (err = deathadder_chroma_send_get_firmware_command(m)) ||
  665. (err = deathadder_chroma_send_get_serial_no_command(m)) ||
  666. (err = deathadder_chroma_send_set_frequency_command(m)) ||
  667. (err =
  668. deathadder_chroma_send_set_led_state_command(m, scroll_led)) ||
  669. (err =
  670. deathadder_chroma_send_set_led_mode_command(m, scroll_led)) ||
  671. (err =
  672. deathadder_chroma_send_set_led_color_command(m, scroll_led)) ||
  673. (err = deathadder_chroma_send_set_led_state_command(m, logo_led)) ||
  674. (err = deathadder_chroma_send_set_led_mode_command(m, logo_led)) ||
  675. (err = deathadder_chroma_send_set_led_color_command(m, logo_led))) {
  676. m->release(m);
  677. free(drv_data);
  678. return err;
  679. }
  680. m->release(m);
  681. drv_data->profile = (struct razer_mouse_profile){
  682. .mouse = m,
  683. .get_freq = deathadder_chroma_get_freq,
  684. .set_freq = deathadder_chroma_set_freq,
  685. .get_dpimapping = deathadder_chroma_get_dpimapping,
  686. .set_dpimapping = deathadder_chroma_set_dpimapping};
  687. razer_generic_usb_gen_idstr(usbdev, m->usb_ctx->h,
  688. DEATHADDER_CHROMA_DEVICE_NAME, false,
  689. drv_data->serial, m->idstr);
  690. m->type = RAZER_MOUSETYPE_DEATHADDER;
  691. m->get_fw_version = deathadder_chroma_get_fw_version;
  692. m->global_get_leds = deathadder_chroma_get_leds;
  693. m->get_profiles = deathadder_chroma_get_profiles;
  694. m->supported_axes = deathadder_chroma_supported_axes;
  695. m->supported_resolutions = deathadder_chroma_supported_resolutions;
  696. m->supported_freqs = deathadder_chroma_supported_freqs;
  697. m->supported_dpimappings = deathadder_chroma_supported_dpimappings;
  698. return 0;
  699. }
  700. void razer_deathadder_chroma_release(struct razer_mouse *m)
  701. {
  702. struct deathadder_chroma_driver_data *drv_data;
  703. drv_data = m->drv_data;
  704. free(drv_data);
  705. m->drv_data = NULL;
  706. }