/* * Lowlevel hardware access for the * Razer Synapse wire protocol * * Important notice: * This hardware driver is based on reverse engineering, only. * * Copyright (C) 2012 Michael Buesch * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include "librazer.h" #include "synapse.h" #include "razer_private.h" #include "util.h" #include "buttonmapping.h" enum synapse_constants { SYNAPSE_NR_PROFILES = 5, SYNAPSE_NR_DPIMAPPINGS = 5, SYNAPSE_NR_AXES = 3, SYNAPSE_NR_LEDS = 2, SYNAPSE_SERIAL_MAX_LEN = 32, SYNAPSE_PROFNAME_MAX_LEN = 20, }; enum synapse_phys_button { /* Physical button IDs */ SYNAPSE_PHYSBUT_LEFT = 0x01, /* Left button */ SYNAPSE_PHYSBUT_RIGHT, /* Right button */ SYNAPSE_PHYSBUT_MIDDLE, /* Middle button */ SYNAPSE_PHYSBUT_LFRONT, /* Left side, front button */ SYNAPSE_PHYSBUT_LREAR, /* Left side, rear button */ SYNAPSE_PHYSBUT_RFRONT, /* Right side, front button */ SYNAPSE_PHYSBUT_RREAR, /* Right side, rear button */ SYNAPSE_PHYSBUT_TFRONT, /* Top side, front button */ SYNAPSE_PHYSBUT_TREAR, /* Top side, rear button */ SYNAPSE_PHYSBUT_SCROLLUP, /* Scroll wheel up */ SYNAPSE_PHYSBUT_SCROLLDWN, /* Scroll wheel down */ NR_SYNAPSE_PHYSBUT = 11, /* Number of physical buttons */ }; struct synapse_request { uint8_t magic; uint8_t flags; uint8_t rw; uint8_t command; uint8_t request; uint8_t _padding[3]; uint8_t payload[80]; le16_t checksum; } _packed; #define SYNAPSE_REQ_MAGIC 0x01 #define SYNAPSE_REQ_FLG_TRANSOK 0x02 #define SYNAPSE_REQ_READ 0x01 #define SYNAPSE_REQ_WRITE 0x00 struct synapse_request_devinfo { uint8_t serial[SYNAPSE_SERIAL_MAX_LEN]; uint8_t fwver[2]; } _packed; struct synapse_request_globconfig { uint8_t profile; uint8_t freq; uint8_t dpisel; uint8_t dpival0; uint8_t dpival1; } _packed; struct synapse_request_profname { uint8_t profile; union { uint8_t name_raw[SYNAPSE_PROFNAME_MAX_LEN * 2]; /* UTF-16-LE */ le16_t name_le16[SYNAPSE_PROFNAME_MAX_LEN]; } _packed; } _packed; struct synapse_one_dpimapping { uint8_t dpival0; uint8_t dpival1; } _packed; struct synapse_led_color { uint8_t r; uint8_t g; uint8_t b; uint8_t padding; } _packed; #define SYNAPSE_LED_COLOR_PADDING 0xFF struct synapse_request_hwconfig { uint8_t profile; uint8_t leds; uint8_t dpisel; uint8_t nr_dpimappings; struct synapse_one_dpimapping dpimappings[SYNAPSE_NR_DPIMAPPINGS]; uint8_t _padding[6]; uint8_t buttonmap[4 * NR_SYNAPSE_PHYSBUT]; struct synapse_led_color led_colors[SYNAPSE_NR_LEDS]; } _packed; struct synapse_buttons { struct razer_buttonmapping mapping[NR_SYNAPSE_PHYSBUT]; }; struct synapse_prof_name { razer_utf16_t name[SYNAPSE_PROFNAME_MAX_LEN + 1]; }; struct synapse_led_name { char name[32]; }; struct razer_synapse { struct razer_mouse *m; void *drv_data; /* Feature selection bits */ unsigned int features; /* Firmware version */ uint16_t fw_version; /* Device serial number */ char serial[SYNAPSE_SERIAL_MAX_LEN + 1]; /* LED names */ struct synapse_led_name led_names[SYNAPSE_NR_LEDS]; /* The currently set LED states. */ enum razer_led_state led_states[SYNAPSE_NR_PROFILES][SYNAPSE_NR_LEDS]; /* LED colors */ struct razer_rgb_color led_colors[SYNAPSE_NR_PROFILES][SYNAPSE_NR_LEDS]; /* The active profile. */ struct razer_mouse_profile *cur_profile; /* Profile configuration (one per profile). */ struct razer_mouse_profile profiles[SYNAPSE_NR_PROFILES]; /* Profile names */ struct synapse_prof_name profile_names[SYNAPSE_NR_PROFILES]; /* Supported mouse axes */ struct razer_axis axes[SYNAPSE_NR_AXES]; /* The active DPI mapping; per profile. */ struct razer_mouse_dpimapping *cur_dpimapping[SYNAPSE_NR_PROFILES]; /* The possible DPI mappings. */ struct razer_mouse_dpimapping dpimappings[SYNAPSE_NR_PROFILES][SYNAPSE_NR_DPIMAPPINGS]; /* The active scan frequency. */ enum razer_mouse_freq cur_freq; /* The active button mapping; per profile. */ struct synapse_buttons buttons[SYNAPSE_NR_PROFILES]; bool commit_pending; }; /* A list of physical buttons on the device. */ static struct razer_button synapse_physical_buttons[] = { { .id = SYNAPSE_PHYSBUT_LEFT, .name = "Leftclick", }, { .id = SYNAPSE_PHYSBUT_RIGHT, .name = "Rightclick", }, { .id = SYNAPSE_PHYSBUT_MIDDLE, .name = "Middleclick", }, { .id = SYNAPSE_PHYSBUT_LFRONT, .name = "Leftside front", }, { .id = SYNAPSE_PHYSBUT_LREAR, .name = "Leftside rear", }, { .id = SYNAPSE_PHYSBUT_RFRONT, .name = "Rightside front", }, { .id = SYNAPSE_PHYSBUT_RREAR, .name = "Rightside rear", }, { .id = SYNAPSE_PHYSBUT_TFRONT, .name = "Top front", }, { .id = SYNAPSE_PHYSBUT_TREAR, .name = "Top rear", }, { .id = SYNAPSE_PHYSBUT_SCROLLUP, .name = "Scroll up", }, { .id = SYNAPSE_PHYSBUT_SCROLLDWN, .name = "Scroll down", }, }; /* A list of possible button functions. */ static struct razer_button_function synapse_button_functions[] = { BUTTONFUNC_LEFT, BUTTONFUNC_RIGHT, BUTTONFUNC_MIDDLE, BUTTONFUNC_PROFDOWN, BUTTONFUNC_PROFUP, BUTTONFUNC_DPIUP, BUTTONFUNC_DPIDOWN, BUTTONFUNC_DPI1, BUTTONFUNC_DPI2, BUTTONFUNC_DPI3, BUTTONFUNC_DPI4, BUTTONFUNC_DPI5, BUTTONFUNC_WIN5, BUTTONFUNC_WIN4, BUTTONFUNC_SCROLLUP, BUTTONFUNC_SCROLLDWN, }; /* requests: * description: command, request / payload * ================================================== * * get device info: 02, 01 * ->reply: 02, 22 / struct synapse_request_devinfo * * get global config: 05, 01 or * 05, 00 * ->reply: 05, 05 / struct synapse_request_globconfig * * set global config: 05, 05 * * get prof name: 22, 01 / profnr * ->reply: 22, 29 / struct synapse_request_profname * * set prof name: 22, 29 / struct synapse_request_profile * * get hwconfig: 06, 01 / profnr * ->reply: 06, 38 / struct synapse_request_hwconfig * * set hwconfig: 06, 48 / struct synapse_request_hwconfig * * get ???: 08, 00 * ->reply: 08, 04 / all zero */ static le16_t synapse_checksum(const struct synapse_request *req) { uint16_t checksum; checksum = razer_xor8_checksum((const uint8_t *)req + 2, sizeof(*req) - 4); if (!(req->flags & SYNAPSE_REQ_FLG_TRANSOK)) checksum |= 0x100; return cpu_to_le16(checksum); } static int synapse_usb_write(struct razer_synapse *s, int request, int command, int index, void *buf, size_t size) { int err; err = libusb_control_transfer( s->m->usb_ctx->h, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, request, command, index, buf, size, RAZER_USB_TIMEOUT); if (err < 0 || (size_t)err != size) { razer_error("synapse: usb_write failed\n"); return -EIO; } razer_msleep(5); return 0; } static int synapse_usb_read(struct razer_synapse *s, int request, int command, int index, void *buf, size_t size) { int err; err = libusb_control_transfer( s->m->usb_ctx->h, LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, request, command, index, buf, size, RAZER_USB_TIMEOUT); if (err < 0 || (size_t)err != size) { razer_error("synapse: usb_read failed\n"); return -EIO; } razer_msleep(5); return 0; } static int synapse_request_send(struct razer_synapse *s, const struct synapse_request *_req) { struct synapse_request req = *_req; req.magic = SYNAPSE_REQ_MAGIC; req.checksum = synapse_checksum(&req); // razer_dump("WR", &req, sizeof(req)); return synapse_usb_write(s, LIBUSB_REQUEST_SET_CONFIGURATION, 0x300, 0, &req, sizeof(req)); } static int synapse_request_receive(struct razer_synapse *s, struct synapse_request *req, bool do_checksum) { le16_t checksum; int err; memset(req, 0, sizeof(*req)); err = synapse_usb_read(s, LIBUSB_REQUEST_CLEAR_FEATURE, 0x300, 0, req, sizeof(*req)); if (err) return err; // razer_dump("RD", req, sizeof(*req)); if (do_checksum) { checksum = synapse_checksum(req); if (req->checksum != checksum) { razer_error("synapse: Received request with invalid " "checksum (was 0x%04X, expected 0x%04X)\n", le16_to_cpu(req->checksum), le16_to_cpu(checksum)); return -EIO; } } return 0; } static int synapse_request_write(struct razer_synapse *s, uint8_t command, uint8_t request, const void *payload, size_t payload_len) { struct synapse_request req, nullreq; int err; if (WARN_ON(payload_len > sizeof(req.payload))) return -EINVAL; memset(&req, 0, sizeof(req)); req.rw = SYNAPSE_REQ_WRITE; req.command = command; req.request = request; if (payload) memcpy(req.payload, payload, payload_len); err = synapse_request_send(s, &req); if (err) return err; err = synapse_request_receive(s, &req, 0); if (err) return err; memset(&nullreq, 0, sizeof(nullreq)); err = synapse_request_send(s, &nullreq); if (err) return err; if (req.magic != SYNAPSE_REQ_MAGIC) { razer_error("synapse: Invalid magic on sent request\n"); return -EIO; } if (req.rw != SYNAPSE_REQ_WRITE) { razer_error("synapse: Invalid rw flag on sent request\n"); return -EIO; } if (req.command != command || req.request != request) { razer_error("synapse: Invalid command on sent request\n"); return -EIO; } return 0; } static int synapse_request_read(struct razer_synapse *s, uint8_t command, uint8_t request, void *payload, size_t payload_len) { struct synapse_request req, nullreq; int err; if (WARN_ON(payload_len > sizeof(req.payload))) return -EINVAL; memset(&req, 0, sizeof(req)); req.rw = SYNAPSE_REQ_READ; req.command = command; req.request = request; if (payload) memcpy(req.payload, payload, payload_len); err = synapse_request_send(s, &req); if (err) return err; err = synapse_request_receive(s, &req, 0); if (err) return err; memset(&nullreq, 0, sizeof(nullreq)); err = synapse_request_send(s, &nullreq); if (err) return err; if (payload) memcpy(payload, req.payload, payload_len); if (req.magic != SYNAPSE_REQ_MAGIC) { razer_error("synapse: Invalid magic on received request\n"); return -EIO; } if (!(req.flags & SYNAPSE_REQ_FLG_TRANSOK)) { razer_error("synapse: Failed to receive request. (TRANSOK flag)\n"); return -EIO; } if (req.rw != SYNAPSE_REQ_READ) { razer_error("synapse: Invalid rw flag on received request\n"); return -EIO; } if (req.command != command) { razer_error("synapse: Invalid command on received request\n"); return -EIO; } return 0; } static int synapse_read_config_from_hw(struct razer_synapse *s) { unsigned int i, j; int err; struct synapse_request_profname profname; struct synapse_request_globconfig globconfig; struct synapse_request_hwconfig hwconfig; enum razer_mouse_res res_x, res_y; /* Get global config */ memset(&globconfig, 0, sizeof(globconfig)); err = synapse_request_read(s, 5, 1, &globconfig, sizeof(globconfig)); if (err) return err; if (globconfig.profile < 1 || globconfig.profile > SYNAPSE_NR_PROFILES) { razer_error("synapse: Got invalid profile number: %u\n", (unsigned int)globconfig.profile); globconfig.profile = 1; } s->cur_profile = &s->profiles[globconfig.profile - 1]; switch (globconfig.freq) { case 1: s->cur_freq = RAZER_MOUSE_FREQ_1000HZ; break; case 2: s->cur_freq = RAZER_MOUSE_FREQ_500HZ; break; case 8: s->cur_freq = RAZER_MOUSE_FREQ_125HZ; break; default: razer_error("synapse: " "Read invalid frequency value from device (%u)\n", globconfig.freq); s->cur_freq = RAZER_MOUSE_FREQ_125HZ; } /* Get the profile names */ for (i = 0; i < SYNAPSE_NR_PROFILES; i++) { memset(&profname, 0, sizeof(profname)); profname.profile = i + 1; err = synapse_request_read(s, 0x22, 1, &profname, sizeof(profname)); if (err) return err; memset(&s->profile_names[i], 0, sizeof(s->profile_names[i])); for (j = 0; j < SYNAPSE_PROFNAME_MAX_LEN; j++) { s->profile_names[i].name[j] = profname.name_raw[j * 2 + 0]; s->profile_names[i].name[j] |= (uint16_t)profname.name_raw[j * 2 + 1] << 8; } } /* Get the profile configs */ for (i = 0; i < SYNAPSE_NR_PROFILES; i++) { memset(&hwconfig, 0, sizeof(hwconfig)); hwconfig.profile = i + 1; err = synapse_request_read(s, 6, 1, &hwconfig, sizeof(hwconfig)); if (err) return err; if (hwconfig.profile != i + 1) { razer_error("synapse: Failed to read hw config (%u vs %u)\n", hwconfig.profile, i + 1); hwconfig.profile = i + 1; } for (j = 0; j < SYNAPSE_NR_LEDS; j++) s->led_states[i][j] = !!(hwconfig.leds & (1 << j)); if (hwconfig.nr_dpimappings < 1 || hwconfig.nr_dpimappings > SYNAPSE_NR_DPIMAPPINGS) { razer_error("synapse: Got invalid nr_dpimappings: %u\n", hwconfig.nr_dpimappings); hwconfig.nr_dpimappings = SYNAPSE_NR_DPIMAPPINGS; } if (hwconfig.dpisel < 1 || hwconfig.dpisel > SYNAPSE_NR_DPIMAPPINGS || hwconfig.dpisel > hwconfig.nr_dpimappings) { razer_error("synapse: Got invalid DPI selection: %u\n", hwconfig.dpisel); hwconfig.dpisel = 1; } s->cur_dpimapping[i] = &s->dpimappings[i][hwconfig.dpisel - 1]; for (j = 0; j < SYNAPSE_NR_DPIMAPPINGS; j++) { if (j + 1 > hwconfig.nr_dpimappings) { res_x = RAZER_MOUSE_RES_5600DPI; res_y = res_x; } else { res_x = ((hwconfig.dpimappings[j].dpival0 / 4) + 1) * 100; res_y = ((hwconfig.dpimappings[j].dpival1 / 4) + 1) * 100; } s->dpimappings[i][j].res[RAZER_DIM_X] = res_x; s->dpimappings[i][j].res[RAZER_DIM_Y] = res_y; } err = razer_parse_buttonmap(hwconfig.buttonmap, sizeof(hwconfig.buttonmap), s->buttons[i].mapping, ARRAY_SIZE(s->buttons[i].mapping), 2); if (err) return err; for (j = 0; j < SYNAPSE_NR_LEDS; j++) { s->led_colors[i][j].r = hwconfig.led_colors[j].r; s->led_colors[i][j].g = hwconfig.led_colors[j].g; s->led_colors[i][j].b = hwconfig.led_colors[j].b; s->led_colors[i][j].valid = !!(s->features & RAZER_SYNFEAT_RGBLEDS); } } return 0; } static int synapse_read_devinfo(struct razer_synapse *s) { struct synapse_request_devinfo devinfo; int err; s->fw_version = 0; memset(s->serial, 0, sizeof(s->serial)); memset(&devinfo, 0, sizeof(devinfo)); err = synapse_request_read(s, 2, 1, &devinfo, sizeof(devinfo)); if (err) return -EIO; s->fw_version = ((uint16_t)(devinfo.fwver[0]) << 8) | devinfo.fwver[1]; memcpy(s->serial, devinfo.serial, SYNAPSE_SERIAL_MAX_LEN); return 0; } static int synapse_get_fw_version(struct razer_mouse *m) { struct razer_synapse *s = m->drv_data; return s->fw_version; } static int synapse_do_commit(struct razer_synapse *s) { struct synapse_request_profname profname; struct synapse_request_globconfig globconfig; struct synapse_request_hwconfig hwconfig; int err; unsigned int i, j; /* Commit profile configs */ for (i = 0; i < SYNAPSE_NR_PROFILES; i++) { memset(&hwconfig, 0, sizeof(hwconfig)); hwconfig.profile = i + 1; hwconfig.leds = 0x04; /* Bit 2 is always set */ for (j = 0; j < SYNAPSE_NR_LEDS; j++) { if (s->led_states[i][j]) hwconfig.leds |= (1 << j); } hwconfig.dpisel = (s->cur_dpimapping[i]->nr % 10) + 1; hwconfig.nr_dpimappings = SYNAPSE_NR_DPIMAPPINGS; for (j = 0; j < SYNAPSE_NR_DPIMAPPINGS; j++) { hwconfig.dpimappings[j].dpival0 = ((s->dpimappings[i][j].res[RAZER_DIM_X] / 100) - 1) * 4; hwconfig.dpimappings[j].dpival1 = ((s->dpimappings[i][j].res[RAZER_DIM_Y] / 100) - 1) * 4; } err = razer_create_buttonmap(hwconfig.buttonmap, sizeof(hwconfig.buttonmap), s->buttons[i].mapping, ARRAY_SIZE(s->buttons[i].mapping), 2); if (err) return err; if (s->features & RAZER_SYNFEAT_RGBLEDS) { for (j = 0; j < SYNAPSE_NR_LEDS; j++) { hwconfig.led_colors[j].padding = SYNAPSE_LED_COLOR_PADDING; hwconfig.led_colors[j].r = s->led_colors[i][j].r; hwconfig.led_colors[j].g = s->led_colors[i][j].g; hwconfig.led_colors[j].b = s->led_colors[i][j].b; } } err = synapse_request_write(s, 6, 0x48, &hwconfig, sizeof(hwconfig)); if (err) return err; } /* Commit profile names */ for (i = 0; i < SYNAPSE_NR_PROFILES; i++) { memset(&profname, 0, sizeof(profname)); profname.profile = i + 1; for (j = 0; j < SYNAPSE_PROFNAME_MAX_LEN; j++) { le16_t c = cpu_to_le16(s->profile_names[i].name[j]); profname.name_le16[j] = c; } err = synapse_request_write(s, 0x22, 0x29, &profname, sizeof(profname)); if (err) return err; } /* Commit global config */ memset(&globconfig, 0, sizeof(globconfig)); globconfig.profile = s->cur_profile->nr + 1; switch (s->cur_freq) { default: case RAZER_MOUSE_FREQ_1000HZ: globconfig.freq = 1; break; case RAZER_MOUSE_FREQ_500HZ: globconfig.freq = 2; break; case RAZER_MOUSE_FREQ_125HZ: globconfig.freq = 8; break; } globconfig.dpisel = (s->cur_dpimapping[s->cur_profile->nr]->nr % 10) + 1; globconfig.dpival0 = ((s->cur_dpimapping[s->cur_profile->nr]->res[RAZER_DIM_X] / 100) - 1) * 4; globconfig.dpival1 = ((s->cur_dpimapping[s->cur_profile->nr]->res[RAZER_DIM_Y] / 100) - 1) * 4; err = synapse_request_write(s, 5, 5, &globconfig, sizeof(globconfig)); if (err) return err; return 0; } static int synapse_commit(struct razer_mouse *m, int force) { struct razer_synapse *s = m->drv_data; int err = 0; if (!m->claim_count) return -EBUSY; if (s->commit_pending || force) { err = synapse_do_commit(s); if (!err) s->commit_pending = 0; } return err; } static enum razer_mouse_freq synapse_global_get_freq(struct razer_mouse *m) { struct razer_synapse *s = m->drv_data; return s->cur_freq; } static int synapse_global_set_freq(struct razer_mouse *m, enum razer_mouse_freq freq) { struct razer_synapse *s = m->drv_data; if (!s->m->claim_count) return -EBUSY; s->cur_freq = freq; s->commit_pending = 1; return 0; } static const razer_utf16_t * synapse_profile_get_name(struct razer_mouse_profile *p) { struct razer_mouse *m = p->mouse; struct razer_synapse *s = m->drv_data; if (p->nr >= SYNAPSE_NR_PROFILES) return NULL; return s->profile_names[p->nr].name; } static int synapse_profile_set_name(struct razer_mouse_profile *p, const razer_utf16_t *new_name) { struct razer_mouse *m = p->mouse; struct razer_synapse *s = m->drv_data; int err; if (p->nr >= SYNAPSE_NR_PROFILES) return -EINVAL; if (!m->claim_count) return -EBUSY; err = razer_utf16_cpy(s->profile_names[p->nr].name, new_name, SYNAPSE_PROFNAME_MAX_LEN); s->commit_pending = 1; return err; } static int synapse_led_toggle(struct razer_led *led, enum razer_led_state new_state) { struct razer_mouse_profile *p = led->u.mouse_prof; struct razer_mouse *m = p->mouse; struct razer_synapse *s = m->drv_data; if (led->id >= SYNAPSE_NR_LEDS) return -EINVAL; if ((new_state != RAZER_LED_OFF) && (new_state != RAZER_LED_ON)) return -EINVAL; if (p->nr >= SYNAPSE_NR_PROFILES) return -EINVAL; if (!s->m->claim_count) return -EBUSY; s->led_states[p->nr][led->id] = new_state; s->commit_pending = 1; return 0; } static int synapse_led_change_color(struct razer_led *led, const struct razer_rgb_color *new_color) { struct razer_mouse_profile *p = led->u.mouse_prof; struct razer_mouse *m = p->mouse; struct razer_synapse *s = m->drv_data; if (led->id >= SYNAPSE_NR_LEDS) return -EINVAL; if (p->nr >= SYNAPSE_NR_PROFILES) return -EINVAL; if (!s->m->claim_count) return -EBUSY; s->led_colors[p->nr][led->id] = *new_color; s->commit_pending = 1; return 0; } static struct razer_mouse_profile * synapse_get_profiles(struct razer_mouse *m) { struct razer_synapse *s = m->drv_data; return &s->profiles[0]; } static struct razer_mouse_profile * synapse_get_active_profile(struct razer_mouse *m) { struct razer_synapse *s = m->drv_data; return s->cur_profile; } static int synapse_set_active_profile(struct razer_mouse *m, struct razer_mouse_profile *p) { struct razer_synapse *s = m->drv_data; if (!s->m->claim_count) return -EBUSY; s->cur_profile = p; s->commit_pending = 1; return 0; } static int synapse_supported_dpimappings(struct razer_mouse *m, struct razer_mouse_dpimapping **res_ptr) { struct razer_synapse *s = m->drv_data; *res_ptr = &s->dpimappings[0][0]; return SYNAPSE_NR_PROFILES * SYNAPSE_NR_DPIMAPPINGS; } static struct razer_mouse_dpimapping * synapse_get_dpimapping(struct razer_mouse_profile *p, struct razer_axis *axis) { struct razer_synapse *s = p->mouse->drv_data; if (p->nr >= ARRAY_SIZE(s->cur_dpimapping)) return NULL; return s->cur_dpimapping[p->nr]; } static int synapse_set_dpimapping(struct razer_mouse_profile *p, struct razer_axis *axis, struct razer_mouse_dpimapping *d) { struct razer_synapse *s = p->mouse->drv_data; razer_id_mask_t idmask; if (!s->m->claim_count) return -EBUSY; if (p->nr >= ARRAY_SIZE(s->cur_dpimapping)) return -EINVAL; razer_id_mask_zero(&idmask); razer_id_mask_set(&idmask, p->nr); if (d->profile_mask != idmask) return -EINVAL; s->cur_dpimapping[p->nr] = d; s->commit_pending = 1; return 0; } static int synapse_dpimapping_modify(struct razer_mouse_dpimapping *d, enum razer_dimension dim, enum razer_mouse_res res) { struct razer_synapse *s = d->mouse->drv_data; if ((int)dim < 0 || (unsigned int)dim >= ARRAY_SIZE(d->res)) return -EINVAL; if (!s->m->claim_count) return -EBUSY; d->res[dim] = res; s->commit_pending = 1; return 0; } static int synapse_profile_get_leds(struct razer_mouse_profile *p, struct razer_led **leds_list) { struct razer_synapse *s = p->mouse->drv_data; struct razer_led *leds[SYNAPSE_NR_LEDS]; int i; if (p->nr >= SYNAPSE_NR_PROFILES) return -EINVAL; for (i = 0; i < SYNAPSE_NR_LEDS; i++) { leds[i] = zalloc(sizeof(struct razer_led)); if (!leds[i]) { for (i--; i >= 0; i--) razer_free(leds[i], sizeof(struct razer_led)); return -ENOMEM; } } for (i = 0; i < SYNAPSE_NR_LEDS; i++) { leds[i]->name = s->led_names[i].name; leds[i]->id = i; leds[i]->state = s->led_states[p->nr][i]; leds[i]->toggle_state = synapse_led_toggle; if (s->features & RAZER_SYNFEAT_RGBLEDS) { leds[i]->color = s->led_colors[p->nr][i]; leds[i]->change_color = synapse_led_change_color; } leds[i]->u.mouse_prof = &s->profiles[p->nr]; } /* Link the list */ *leds_list = leds[0]; for (i = 0; i < SYNAPSE_NR_LEDS; i++) leds[i]->next = (i < SYNAPSE_NR_LEDS - 1) ? leds[i + 1] : NULL; return SYNAPSE_NR_LEDS; } static int synapse_supported_axes(struct razer_mouse *m, struct razer_axis **axes_list) { struct razer_synapse *s = m->drv_data; *axes_list = s->axes; return ARRAY_SIZE(s->axes); } static int synapse_supported_freqs(struct razer_mouse *m, enum razer_mouse_freq **freq_list) { enum razer_mouse_freq *list; const int count = 3; list = malloc(sizeof(*list) * count); if (!list) return -ENOMEM; list[0] = RAZER_MOUSE_FREQ_1000HZ; list[1] = RAZER_MOUSE_FREQ_500HZ; list[2] = RAZER_MOUSE_FREQ_125HZ; *freq_list = list; return count; } static int synapse_supported_resolutions(struct razer_mouse *m, enum razer_mouse_res **res_list) { enum razer_mouse_res *list; unsigned int i, count = 0, res, step = 0; count = 56; step = RAZER_MOUSE_RES_100DPI; list = malloc(sizeof(*list) * count); if (!list) return -ENOMEM; res = step; for (i = 0; i < count; i++) { list[i] = res; res += step; } *res_list = list; return count; } static int synapse_supported_buttons(struct razer_mouse *m, struct razer_button **res_ptr) { *res_ptr = synapse_physical_buttons; return ARRAY_SIZE(synapse_physical_buttons); } static int synapse_supported_button_functions(struct razer_mouse *m, struct razer_button_function **res_ptr) { *res_ptr = synapse_button_functions; return ARRAY_SIZE(synapse_button_functions); } static struct razer_button_function * synapse_get_button_function(struct razer_mouse_profile *p, struct razer_button *b) { struct razer_synapse *s = p->mouse->drv_data; struct synapse_buttons *buttons; if (p->nr > ARRAY_SIZE(s->buttons)) return NULL; buttons = &s->buttons[p->nr]; return razer_get_buttonfunction_by_button( buttons->mapping, ARRAY_SIZE(buttons->mapping), synapse_button_functions, ARRAY_SIZE(synapse_button_functions), b); } static int synapse_set_button_function(struct razer_mouse_profile *p, struct razer_button *b, struct razer_button_function *f) { struct razer_synapse *s = p->mouse->drv_data; struct synapse_buttons *buttons; struct razer_buttonmapping *mapping; if (!s->m->claim_count) return -EBUSY; if (p->nr > ARRAY_SIZE(s->buttons)) return -EINVAL; buttons = &s->buttons[p->nr]; mapping = razer_get_buttonmapping_by_physid( buttons->mapping, ARRAY_SIZE(buttons->mapping), b->id); if (!mapping) return -ENODEV; mapping->logical = f->id; s->commit_pending = 1; return 0; } int razer_synapse_init(struct razer_mouse *m, void *drv_data, unsigned int features) { struct razer_synapse *s; int i, j, k, err; BUILD_BUG_ON(sizeof(struct synapse_request) != 90); BUILD_BUG_ON(sizeof(struct synapse_request_devinfo) != 34); BUILD_BUG_ON(sizeof(struct synapse_request_globconfig) != 5); BUILD_BUG_ON(sizeof(struct synapse_request_profname) != 41); BUILD_BUG_ON(sizeof(struct synapse_request_hwconfig) != 72); s = zalloc(sizeof(*s)); if (!s) return -ENOMEM; m->drv_data = s; s->m = m; s->drv_data = drv_data; s->features = features; err = razer_usb_add_used_interface(m->usb_ctx, 0, 0); if (err) { err = -ENODEV; goto err_free; } for (i = 0; i < SYNAPSE_NR_PROFILES; i++) { s->profiles[i].nr = i; s->profiles[i].get_leds = synapse_profile_get_leds; s->profiles[i].get_name = synapse_profile_get_name; s->profiles[i].set_name = synapse_profile_set_name; s->profiles[i].get_dpimapping = synapse_get_dpimapping; s->profiles[i].set_dpimapping = synapse_set_dpimapping; s->profiles[i].get_button_function = synapse_get_button_function; s->profiles[i].set_button_function = synapse_set_button_function; s->profiles[i].mouse = m; } razer_init_axes(&s->axes[0], "X", 0, "Y", 0, "Scroll", 0); for (i = 0; i < SYNAPSE_NR_PROFILES; i++) { for (j = 0; j < SYNAPSE_NR_DPIMAPPINGS; j++) { s->dpimappings[i][j].nr = i * 10 + j; for (k = 0; k < RAZER_NR_DIMS; k++) s->dpimappings[i][j].res[k] = RAZER_MOUSE_RES_UNKNOWN; s->dpimappings[i][j].dimension_mask = (1 << RAZER_DIM_X) | (1 << RAZER_DIM_Y); razer_id_mask_zero(&s->dpimappings[i][j].profile_mask); razer_id_mask_set(&s->dpimappings[i][j].profile_mask, i); s->dpimappings[i][j].change = synapse_dpimapping_modify; s->dpimappings[i][j].mouse = m; } } /* Default LED names */ razer_synapse_set_led_name(m, 0, "ScrollWheel"); razer_synapse_set_led_name(m, 1, "GlowingLogo"); err = m->claim(m); if (err) { razer_error("synapse: " "Failed to initially claim the device\n"); goto err_free; } err = synapse_read_devinfo(s); if (err) { razer_error("synapse: Failed to get firmware version\n"); goto err_release; } err = synapse_read_config_from_hw(s); if (err) { razer_error("synapse: " "Failed to read the configuration from hardware\n"); goto err_release; } m->get_fw_version = synapse_get_fw_version; m->commit = synapse_commit; m->global_get_freq = synapse_global_get_freq; m->global_set_freq = synapse_global_set_freq; m->nr_profiles = SYNAPSE_NR_PROFILES; m->get_profiles = synapse_get_profiles; m->get_active_profile = synapse_get_active_profile; m->set_active_profile = synapse_set_active_profile; m->supported_axes = synapse_supported_axes; m->supported_resolutions = synapse_supported_resolutions; m->supported_freqs = synapse_supported_freqs; m->supported_dpimappings = synapse_supported_dpimappings; m->supported_buttons = synapse_supported_buttons; m->supported_button_functions = synapse_supported_button_functions; err = synapse_do_commit(s); if (err) { razer_error("synapse: Failed to commit initial settings\n"); goto err_release; } m->release(m); return 0; err_release: m->release(m); err_free: razer_free(s, sizeof(*s)); return err; } void razer_synapse_exit(struct razer_mouse *m) { struct razer_synapse *s = m->drv_data; razer_free(s, sizeof(*s)); m->drv_data = NULL; } void * razer_synapse_get_drv_data(struct razer_mouse *m) { struct razer_synapse *s = m->drv_data; return s->drv_data; } int razer_synapse_set_led_name(struct razer_mouse *m, unsigned int index, const char *name) { struct razer_synapse *s = m->drv_data; if (index >= ARRAY_SIZE(s->led_names)) return -EINVAL; razer_strlcpy(s->led_names[index].name, name, sizeof(s->led_names[index].name)); return 0; } const char * razer_synapse_get_serial(struct razer_mouse *m) { struct razer_synapse *s = m->drv_data; return s->serial; }