diff --git a/g29-wheel/g29_usb.c b/g29-wheel/g29_usb.c index a27d7f4..e0bb3db 100644 --- a/g29-wheel/g29_usb.c +++ b/g29-wheel/g29_usb.c @@ -144,63 +144,6 @@ static void g29_switch_mode(struct g29_dev *g29, enum g29_mode new_mode) { new_mode == G29_MODE_WASD ? "WASD" : "MOUSE"); } -static int calc_adjusted_steering_distance(int distance) { - /* Apply non-linear steering curve: - * adjusted = (distance / MAX)^(curve/100) * MAX - * - * For curve=200 (squared): 5% input -> 0.25% output, 50% -> 25%, 100% -> 100% - * For curve=150 (^1.5): 5% input -> ~1.1% output, 50% -> ~35%, 100% -> 100% - * For curve=100 (linear): 5% input -> 5% output (no change) - * - * Using integer math: adjusted = (distance^2 / MAX) for curve=200 - */ - if (steer_curve == 100) { - return distance; - } - if (steer_curve == 200) { - return (distance * distance) / WHEEL_MAX_DIST; - } - - /* Generic power curve using normalized values (0-1000 range for precision) - * normalized = (distance * 1000) / WHEEL_MAX_DIST - * Apply power approximation, then scale back - */ - int normalized = (distance * NORMALIZATION_PRECISION) / WHEEL_MAX_DIST; - int powered; - - if (steer_curve == 150) { - /* Approximate ^1.5 with (x * sqrt(x)) */ - int sqrt_norm = int_sqrt(normalized * NORMALIZATION_PRECISION); - powered = (normalized * sqrt_norm) / NORMALIZATION_PRECISION; - } else { - /* Fallback: squared for any other value > 100 */ - powered = (normalized * normalized) / NORMALIZATION_PRECISION; - } - - return (powered * WHEEL_MAX_DIST) / NORMALIZATION_PRECISION; -} - -static int calc_adjusted_pedal_distance(int pedal_pressure, int curve) { - if (curve == 100) { - return pedal_pressure; - } - if (curve == 200) { - return (pedal_pressure * pedal_pressure) / G29_PEDAL_RELEASED; - } - - int normalized = (pedal_pressure * NORMALIZATION_PRECISION) / G29_PEDAL_RELEASED; - int powered; - - if (curve == 150) { - int sqrt_norm = int_sqrt(normalized * NORMALIZATION_PRECISION); - powered = (normalized * sqrt_norm) / NORMALIZATION_PRECISION; - } else { - powered = (normalized * normalized) / NORMALIZATION_PRECISION; - } - - return (powered * G29_PEDAL_RELEASED) / NORMALIZATION_PRECISION; -} - static void mouse_mode_timer_fn(struct timer_list *t) { struct g29_dev *g29 = timer_container_of(g29, t, mouse_timer); @@ -269,64 +212,32 @@ static void mouse_mode_timer_fn(struct timer_list *t) { static void wasd_mode_timer_fn(struct timer_list *t) { struct g29_dev *g29 = timer_container_of(g29, t, steer_timer); + const int period = 50; const int rot = le16_to_cpu(g29->last.rot_le); + const int gas = 0xFF - g29->last.gas; + const int brk = 0xFF - g29->last.brk; - int effective_rot = rot; - int distance_from_center = abs(rot - WHEEL_CENTER); - if (distance_from_center <= steer_deadzone) { - effective_rot = WHEEL_CENTER; - } + const int gas_duty = gas * period / 0x40; + input_report_key(g29->input, KEY_W, g29->gas_phase_accumulator < gas_duty); + g29->gas_phase_accumulator++; + g29->gas_phase_accumulator %= period; - int distance_to_center = abs(effective_rot - WHEEL_CENTER); - int adjusted_distance = calc_adjusted_steering_distance(distance_to_center); + input_report_key(g29->input, KEY_S, brk >= 0x80); - /* Phase accumulator approach: - * Accumulate the adjusted distance on each tick. - * When it exceeds the max distance, press the key and wrap. - * This gives us a duty cycle of (adjusted_distance / WHEEL_MAX_DIST). - * - * Examples (with curve=200): - * 5% steering -> ~0.25% press rate - * 50% steering -> 25% press rate - * 100% steering -> 100% press rate (every tick) - */ - g29->phase_accumulator += adjusted_distance; - - bool press_key; - if (g29->phase_accumulator >= WHEEL_MAX_DIST) { - g29->phase_accumulator -= WHEEL_MAX_DIST; - press_key = true; + if (rot >= 0x7ff8 && rot <= 0x8008){ + input_report_key(g29->input, KEY_A, 0); + input_report_key(g29->input, KEY_D, 0); } else { - press_key = false; + const int mag = rot < 0x8000 ? 0x8000 - rot : rot - 0x8000; + const int duty = mag * period / 0x3000; + const int k1 = rot > 0x8000 ? KEY_D : KEY_A; + const int k2 = rot > 0x8000 ? KEY_A : KEY_D; + input_report_key(g29->input, k2, 0); + input_report_key(g29->input, k1, g29->phase_accumulator < duty); } - input_report_key(g29->input, KEY_A, press_key && (effective_rot < WHEEL_CENTER)); - input_report_key(g29->input, KEY_D, press_key && (effective_rot >= WHEEL_CENTER)); - - /* Gas pedal (0xFF=up, 0x00=down) -> W key */ - int gas_pressure = 0xFF - g29->last.gas; - int gas_adjusted = calc_adjusted_pedal_distance(gas_pressure, gas_curve); - g29->gas_phase_accumulator += gas_adjusted; - - bool press_w = false; - if (g29->gas_phase_accumulator >= G29_PEDAL_RELEASED) { - g29->gas_phase_accumulator -= G29_PEDAL_RELEASED; - press_w = true; - } - - /* Clutch pedal (0xFF=up, 0x00=down) -> S key */ - int clutch_pressure = 0xFF - g29->last.clt; - int clutch_adjusted = calc_adjusted_pedal_distance(clutch_pressure, clutch_curve); - g29->clutch_phase_accumulator += clutch_adjusted; - - bool press_s = false; - if (g29->clutch_phase_accumulator >= G29_PEDAL_RELEASED) { - g29->clutch_phase_accumulator -= G29_PEDAL_RELEASED; - press_s = true; - } - - input_report_key(g29->input, KEY_W, press_w); - input_report_key(g29->input, KEY_S, press_s); + g29->phase_accumulator++; + g29->phase_accumulator %= period; input_sync(g29->input); @@ -335,52 +246,45 @@ static void wasd_mode_timer_fn(struct timer_list *t) { } static void process_media_mode(struct g29_dev *g29, const struct g29_state *cur, const struct g29_state *prev) { - const u32 pressed = le32_to_cpu(cur->buttons_le & ~prev->buttons_le); - const u32 released = le32_to_cpu(~cur->buttons_le & prev->buttons_le); + const u32 buttons = le32_to_cpu(cur->buttons_le); for (int i = 0; i < ARRAY_SIZE(media_mode_keymap); i++) { const struct g29_keymap *k = &media_mode_keymap[i]; - if (pressed & k->mask) { - input_report_key(g29->input, k->keycode, 1); - } - if (released & k->mask) { - input_report_key(g29->input, k->keycode, 0); - } + input_report_key(g29->input, k->keycode, !!(buttons & k->mask)); } input_sync(g29->input); } static void process_wasd_mode(struct g29_dev *g29, const struct g29_state *cur, const struct g29_state *prev) { - /* WASD mode is handled by the timer function (g29_steer_timer_fn) */ - /* No additional processing needed here */ + // WASD mode is handled by the timer function (g29_steer_timer_fn) + const u32 buttons = le32_to_cpu(cur->buttons_le); + input_report_key(g29->input, KEY_C, !!(buttons & G29_BTN_L1)); + input_report_key(g29->input, KEY_SPACE, !!(buttons & G29_BTN_R1)); + input_sync(g29->input); } static void process_mouse_mode(struct g29_dev *g29, const struct g29_state *cur, const struct g29_state *prev) { + const u32 buttons = le32_to_cpu(cur->buttons_le); const u32 pressed = le32_to_cpu(cur->buttons_le & ~prev->buttons_le); const u32 released = le32_to_cpu(~cur->buttons_le & prev->buttons_le); for (int i = 0; i < ARRAY_SIZE(mouse_mode_keymap); i++) { const struct g29_keymap *k = &mouse_mode_keymap[i]; - if (pressed & k->mask) { - input_report_key(g29->input, k->keycode, 1); - } - if (released & k->mask) { - input_report_key(g29->input, k->keycode, 0); - } + input_report_key(g29->input, k->keycode, !!(buttons & k->mask)); } if (pressed & G29_BTN_RED_CW) { input_report_rel(g29->input, REL_WHEEL, 1); - } - if (pressed & G29_BTN_RED_CCW) { + } else if (pressed & G29_BTN_RED_CCW) { input_report_rel(g29->input, REL_WHEEL, -1); + } else { + input_report_rel(g29->input, REL_WHEEL, 0); } input_sync(g29->input); } static void g29_check_mode_switch(struct g29_dev *g29, const struct g29_state *cur, const struct g29_state *prev) { - u32 pressed = le32_to_cpu(cur->buttons_le & ~prev->buttons_le); - + const u32 pressed = le32_to_cpu(cur->buttons_le & ~prev->buttons_le); if (pressed & G29_BTN_SHARE) { g29_switch_mode(g29, G29_MODE_MEDIA); } else if (pressed & G29_BTN_OPTION) { @@ -545,6 +449,8 @@ static int g29_probe(struct usb_interface *intf, const struct usb_device_id *id) input_set_capability(input, EV_KEY, KEY_A); input_set_capability(input, EV_KEY, KEY_S); input_set_capability(input, EV_KEY, KEY_D); + input_set_capability(input, EV_KEY, KEY_C); + input_set_capability(input, EV_KEY, KEY_SPACE); // Mouse mode capabilities input_set_capability(input, EV_KEY, BTN_LEFT); diff --git a/wasd-emulator/Makefile b/wasd-emulator/Makefile deleted file mode 100644 index ddb841b..0000000 --- a/wasd-emulator/Makefile +++ /dev/null @@ -1,25 +0,0 @@ - -.PHONY: all - -obj-m += wasd.o -KVER := $(shell uname -r) -KDIR ?= $(firstword $(wildcard /lib/modules/$(KVER)/build) $(wildcard /usr/lib/modules/$(KVER)/build)) -PWD := $(shell pwd) -OUT := $(PWD)/out - -all: - @if [ -z "$(KDIR)" ]; then \ - echo "ERROR: kernel build dir not found for $(KVER). Install kernel headers (e.g. linux-headers)"; \ - exit 2; \ - fi - mkdir -p $(OUT) - $(MAKE) -C $(KDIR) M=$(PWD) modules - -mv -f -- *.ko *.mod.c *.o .*.o *.mod modules.order .*.cmd *.symvers $(OUT) - -clean: - @if [ -z "$(KDIR)" ]; then \ - echo "ERROR: kernel build dir not found for $(KVER)."; \ - exit 2; \ - fi - $(MAKE) -C $(KDIR) M=$(PWD) clean - rm -rf $(OUT) *.cmd *.order *.mod *.o diff --git a/wasd-emulator/wasd.c b/wasd-emulator/wasd.c deleted file mode 100644 index cf64452..0000000 --- a/wasd-emulator/wasd.c +++ /dev/null @@ -1,223 +0,0 @@ - -#include -#include -#include -#include -#include -#include -#include - -#define DRV_NAME "usb_steeringwheel_wasd" - -struct wheel_evt { - uint32_t buttons_be; - uint16_t rot_be; - uint8_t gas; - uint8_t brk; - uint8_t clt; - uint8_t gr_x; - uint8_t gr_y; - uint8_t gr_z; -}; - -struct wheel { - struct usb_device *udev; - struct usb_interface *intf; - struct input_dev *input; - - struct urb *irq_urb; - struct wheel_evt *irq_data; - dma_addr_t irq_dma; - int irq_len; - int irq_interval; - int irq_ep; - - char phys[64]; - atomic_t opened; -}; - -static int drv_open(struct input_dev *dev) { - struct wheel *w = input_get_drvdata(dev); - if (!w) return -ENODEV; - atomic_set(&w->opened, 1); - int ret; - if ((ret = usb_submit_urb(w->irq_urb, GFP_KERNEL))) { - atomic_set(&w->opened, 0); - return ret; - } - return 0; -} - -static void drv_irq(struct urb *urb) { - // called every 2ms? - struct wheel *w = urb->context; - if (!w || !atomic_read(&w->opened)) - return; - - const int status = urb->status; - if (status) { - if (status == -ENOENT || status == -ECONNRESET || status == -ESHUTDOWN) - return; - dev_dbg(&w->intf->dev, "irq urb status %d\n", status); - goto resubmit; - } - - const struct wheel_evt *data = w->irq_data; - const int rot = be16_to_cpu(data->buttons_be); - // TODO set keys according to ratio - input_report_key(w->input, KEY_W, data->gas <= 0x80); - input_report_key(w->input, KEY_S, data->brk <= 0x80); - input_report_key(w->input, KEY_A, rot <= 0x6000); - input_report_key(w->input, KEY_D, rot >= 0xA000); - input_sync(w->input); - -resubmit: - usb_submit_urb(w->irq_urb, GFP_ATOMIC); -} - -static void drv_close(struct input_dev *dev) { - struct wheel *w = input_get_drvdata(dev); - if (!w) return; - atomic_set(&w->opened, 0); - usb_kill_urb(w->irq_urb); -} - -static int drv_probe(struct usb_interface *intf, const struct usb_device_id *id) { - struct usb_device *udev = interface_to_usbdev(intf); - int ret; - - // Logitech G29 - //if (le16_to_cpu(udev->descriptor.idVendor) != 0x046d || le16_to_cpu(udev->descriptor.idProduct) != 0xc24f) - // return -ENODEV; - - struct usb_endpoint_descriptor *ep = NULL; - const struct usb_host_interface *alts = intf->cur_altsetting; - for (int i = 0; i < alts->desc.bNumEndpoints; i++) { - struct usb_endpoint_descriptor *d = &alts->endpoint[i].desc; - if (usb_endpoint_is_int_in(d)) { - ep = d; - break; - } - } - if (!ep) return -ENODEV; - - struct wheel *w; - if ((w = kzalloc(sizeof(*w), GFP_KERNEL)) == NULL) - return -ENOMEM; - - w->udev = usb_get_dev(udev); - w->intf = intf; - atomic_set(&w->opened, 0); - - w->irq_ep = usb_endpoint_num(ep); - w->irq_len = usb_endpoint_maxp(ep); - w->irq_interval = ep->bInterval; - - if ((w->irq_urb = usb_alloc_urb(0, GFP_KERNEL)) == NULL) { - ret = -ENOMEM; - goto err_free; - } - - if ((w->irq_data = usb_alloc_coherent(udev, w->irq_len, GFP_KERNEL, &w->irq_dma)) == NULL) { - ret = -ENOMEM; - goto err_free_urb; - } - - if ((w->input = input_allocate_device()) == NULL) { - ret = -ENOMEM; - goto err_free_buf; - } - - usb_make_path(udev, w->phys, sizeof(w->phys)); - strlcat(w->phys, "/input0", sizeof(w->phys)); - - w->input->name = "USB Boot Mouse (example driver)"; - w->input->phys = w->phys; - usb_to_input_id(udev, &w->input->id); - w->input->dev.parent = &intf->dev; - - w->input->open = drv_open; - w->input->close = drv_close; - - input_set_drvdata(w->input, w); - - usb_fill_int_urb( - w->irq_urb, - udev, - usb_rcvintpipe(udev, ep->bEndpointAddress), - w->irq_data, - w->irq_len, - drv_irq, - w, - w->irq_interval); - - w->irq_urb->transfer_dma = w->irq_dma; - w->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - - usb_set_intfdata(intf, w); - - if ((ret = input_register_device(w->input)) != 0) - goto err_clear_intfdata; - - dev_info(&intf->dev, - "bound to %04x:%04x, int-in ep 0x%02x maxp %u interval %u\n", - le16_to_cpu(udev->descriptor.idVendor), - le16_to_cpu(udev->descriptor.idProduct), - ep->bEndpointAddress, - w->irq_len, - w->irq_interval); - - return 0; - -err_clear_intfdata: - usb_set_intfdata(intf, NULL); - input_free_device(w->input); - w->input = NULL; -err_free_buf: - usb_free_coherent(udev, w->irq_len, w->irq_data, w->irq_dma); -err_free_urb: - usb_free_urb(w->irq_urb); -err_free: - usb_put_dev(w->udev); - kfree(w); - return ret; -} - -static void drv_disconnect(struct usb_interface *intf) { - struct wheel *w = usb_get_intfdata(intf); - usb_set_intfdata(intf, NULL); - if (!w) return; - - if (w->input) { - input_unregister_device(w->input); - w->input = NULL; - } - - usb_kill_urb(w->irq_urb); - usb_free_coherent(w->udev, w->irq_len, w->irq_data, w->irq_dma); - usb_free_urb(w->irq_urb); - usb_put_dev(w->udev); - kfree(w); - - dev_info(&intf->dev, "disconnected\n"); -} - -static const struct usb_device_id drv_id_table[] = { - { USB_DEVICE_INTERFACE_NUMBER(0x046d, 0xc24f, 0) }, - { USB_INTERFACE_INFO(3, 1, 1) }, - {} -}; -MODULE_DEVICE_TABLE(usb, drv_id_table); - -static struct usb_driver drv = { - .name = DRV_NAME, - .probe = drv_probe, - .disconnect = drv_disconnect, - .id_table = drv_id_table, -}; - -module_usb_driver(drv); - -MODULE_AUTHOR("Lorenz Stechauner"); -MODULE_DESCRIPTION("Steering wheel WASD emulator"); -MODULE_LICENSE("GPL");