Loading drivers/input/joystick/iforce/Kconfig +4 −4 Original line number Diff line number Diff line Loading @@ -13,15 +13,15 @@ config JOYSTICK_IFORCE module will be called iforce. config JOYSTICK_IFORCE_USB bool "I-Force USB joysticks and wheels" depends on JOYSTICK_IFORCE && (JOYSTICK_IFORCE=m || USB=y) && USB tristate "I-Force USB joysticks and wheels" depends on JOYSTICK_IFORCE && USB help Say Y here if you have an I-Force joystick or steering wheel connected to your USB port. config JOYSTICK_IFORCE_232 bool "I-Force Serial joysticks and wheels" depends on JOYSTICK_IFORCE && (JOYSTICK_IFORCE=m || SERIO=y) && SERIO tristate "I-Force Serial joysticks and wheels" depends on JOYSTICK_IFORCE && SERIO help Say Y here if you have an I-Force joystick or steering wheel connected to your serial (COM) port. Loading drivers/input/joystick/iforce/Makefile +3 −4 Original line number Diff line number Diff line Loading @@ -5,7 +5,6 @@ # obj-$(CONFIG_JOYSTICK_IFORCE) += iforce.o iforce-y := iforce-ff.o iforce-main.o iforce-packets.o iforce-$(CONFIG_JOYSTICK_IFORCE_232) += iforce-serio.o iforce-$(CONFIG_JOYSTICK_IFORCE_USB) += iforce-usb.o obj-$(CONFIG_JOYSTICK_IFORCE_232) += iforce-serio.o obj-$(CONFIG_JOYSTICK_IFORCE_USB) += iforce-usb.o drivers/input/joystick/iforce/iforce-main.c +57 −121 Original line number Diff line number Diff line Loading @@ -21,10 +21,11 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <asm/unaligned.h> #include "iforce.h" MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>, Johann Deneux <johann.deneux@gmail.com>"); MODULE_DESCRIPTION("USB/RS232 I-Force joysticks and wheels driver"); MODULE_DESCRIPTION("Core I-Force joysticks and wheels driver"); MODULE_LICENSE("GPL"); static signed short btn_joystick[] = Loading Loading @@ -67,6 +68,7 @@ static struct iforce_device iforce_device[] = { { 0x05ef, 0x8888, "AVB Top Shot Force Feedback Racing Wheel", btn_wheel, abs_wheel, ff_iforce }, //? { 0x061c, 0xc0a4, "ACT LABS Force RS", btn_wheel, abs_wheel, ff_iforce }, //? { 0x061c, 0xc084, "ACT LABS Force RS", btn_wheel, abs_wheel, ff_iforce }, { 0x06a3, 0xff04, "Saitek R440 Force Wheel", btn_wheel, abs_wheel, ff_iforce }, //? { 0x06f8, 0x0001, "Guillemot Race Leader Force Feedback", btn_wheel, abs_wheel, ff_iforce }, //? { 0x06f8, 0x0001, "Guillemot Jet Leader Force Feedback", btn_joystick, abs_joystick_rudder, ff_iforce }, { 0x06f8, 0x0004, "Guillemot Force Feedback Racing Wheel", btn_wheel, abs_wheel, ff_iforce }, //? Loading Loading @@ -132,7 +134,6 @@ static int iforce_upload_effect(struct input_dev *dev, struct ff_effect *effect, * Upload the effect */ switch (effect->type) { case FF_PERIODIC: ret = iforce_upload_periodic(iforce, effect, old); break; Loading Loading @@ -185,15 +186,7 @@ static int iforce_open(struct input_dev *dev) { struct iforce *iforce = input_get_drvdata(dev); switch (iforce->bus) { #ifdef CONFIG_JOYSTICK_IFORCE_USB case IFORCE_USB: iforce->irq->dev = iforce->usbdev; if (usb_submit_urb(iforce->irq, GFP_KERNEL)) return -EIO; break; #endif } iforce->xport_ops->start_io(iforce); if (test_bit(EV_FF, dev->evbit)) { /* Enable force feedback */ Loading Loading @@ -226,27 +219,17 @@ static void iforce_close(struct input_dev *dev) !test_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags)); } switch (iforce->bus) { #ifdef CONFIG_JOYSTICK_IFORCE_USB case IFORCE_USB: usb_kill_urb(iforce->irq); usb_kill_urb(iforce->out); usb_kill_urb(iforce->ctrl); break; #endif #ifdef CONFIG_JOYSTICK_IFORCE_232 case IFORCE_232: //TODO: Wait for the last packets to be sent break; #endif } iforce->xport_ops->stop_io(iforce); } int iforce_init_device(struct iforce *iforce) int iforce_init_device(struct device *parent, u16 bustype, struct iforce *iforce) { struct input_dev *input_dev; struct ff_device *ff; unsigned char c[] = "CEOV"; u8 c[] = "CEOV"; u8 buf[IFORCE_MAX_LENGTH]; size_t len; int i, error; int ff_effects = 0; Loading @@ -264,20 +247,8 @@ int iforce_init_device(struct iforce *iforce) * Input device fields. */ switch (iforce->bus) { #ifdef CONFIG_JOYSTICK_IFORCE_USB case IFORCE_USB: input_dev->id.bustype = BUS_USB; input_dev->dev.parent = &iforce->usbdev->dev; break; #endif #ifdef CONFIG_JOYSTICK_IFORCE_232 case IFORCE_232: input_dev->id.bustype = BUS_RS232; input_dev->dev.parent = &iforce->serio->dev; break; #endif } input_dev->id.bustype = bustype; input_dev->dev.parent = parent; input_set_drvdata(input_dev, iforce); Loading @@ -302,7 +273,7 @@ int iforce_init_device(struct iforce *iforce) */ for (i = 0; i < 20; i++) if (!iforce_get_id_packet(iforce, "O")) if (!iforce_get_id_packet(iforce, 'O', buf, &len)) break; if (i == 20) { /* 5 seconds */ Loading @@ -316,23 +287,23 @@ int iforce_init_device(struct iforce *iforce) * Get device info. */ if (!iforce_get_id_packet(iforce, "M")) input_dev->id.vendor = (iforce->edata[2] << 8) | iforce->edata[1]; if (!iforce_get_id_packet(iforce, 'M', buf, &len) || len < 3) input_dev->id.vendor = get_unaligned_le16(buf + 1); else dev_warn(&iforce->dev->dev, "Device does not respond to id packet M\n"); if (!iforce_get_id_packet(iforce, "P")) input_dev->id.product = (iforce->edata[2] << 8) | iforce->edata[1]; if (!iforce_get_id_packet(iforce, 'P', buf, &len) || len < 3) input_dev->id.product = get_unaligned_le16(buf + 1); else dev_warn(&iforce->dev->dev, "Device does not respond to id packet P\n"); if (!iforce_get_id_packet(iforce, "B")) iforce->device_memory.end = (iforce->edata[2] << 8) | iforce->edata[1]; if (!iforce_get_id_packet(iforce, 'B', buf, &len) || len < 3) iforce->device_memory.end = get_unaligned_le16(buf + 1); else dev_warn(&iforce->dev->dev, "Device does not respond to id packet B\n"); if (!iforce_get_id_packet(iforce, "N")) ff_effects = iforce->edata[1]; if (!iforce_get_id_packet(iforce, 'N', buf, &len) || len < 2) ff_effects = buf[1]; else dev_warn(&iforce->dev->dev, "Device does not respond to id packet N\n"); Loading @@ -348,8 +319,9 @@ int iforce_init_device(struct iforce *iforce) */ for (i = 0; c[i]; i++) if (!iforce_get_id_packet(iforce, c + i)) iforce_dump_packet(iforce, "info", iforce->ecmd, iforce->edata); if (!iforce_get_id_packet(iforce, c[i], buf, &len)) iforce_dump_packet(iforce, "info", (FF_CMD_QUERY & 0xff00) | len, buf); /* * Disable spring, enable force feedback. Loading Loading @@ -383,11 +355,9 @@ int iforce_init_device(struct iforce *iforce) signed short t = iforce->type->abs[i]; switch (t) { case ABS_X: case ABS_Y: case ABS_WHEEL: input_set_abs_params(input_dev, t, -1920, 1920, 16, 128); set_bit(t, input_dev->ffbit); break; Loading @@ -395,12 +365,10 @@ int iforce_init_device(struct iforce *iforce) case ABS_THROTTLE: case ABS_GAS: case ABS_BRAKE: input_set_abs_params(input_dev, t, 0, 255, 0, 0); break; case ABS_RUDDER: input_set_abs_params(input_dev, t, -128, 127, 0, 0); break; Loading @@ -408,7 +376,6 @@ int iforce_init_device(struct iforce *iforce) case ABS_HAT0Y: case ABS_HAT1X: case ABS_HAT1Y: input_set_abs_params(input_dev, t, -1, 1, 0, 0); break; } Loading Loading @@ -443,35 +410,4 @@ int iforce_init_device(struct iforce *iforce) fail: input_free_device(input_dev); return error; } static int __init iforce_init(void) { int err = 0; #ifdef CONFIG_JOYSTICK_IFORCE_USB err = usb_register(&iforce_usb_driver); if (err) return err; #endif #ifdef CONFIG_JOYSTICK_IFORCE_232 err = serio_register_driver(&iforce_serio_drv); #ifdef CONFIG_JOYSTICK_IFORCE_USB if (err) usb_deregister(&iforce_usb_driver); #endif #endif return err; } static void __exit iforce_exit(void) { #ifdef CONFIG_JOYSTICK_IFORCE_USB usb_deregister(&iforce_usb_driver); #endif #ifdef CONFIG_JOYSTICK_IFORCE_232 serio_unregister_driver(&iforce_serio_drv); #endif } module_init(iforce_init); module_exit(iforce_exit); EXPORT_SYMBOL(iforce_init_device); drivers/input/joystick/iforce/iforce-packets.c +70 −145 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <asm/unaligned.h> #include "iforce.h" static struct { Loading Loading @@ -91,27 +92,12 @@ int iforce_send_packet(struct iforce *iforce, u16 cmd, unsigned char* data) /* * If necessary, start the transmission */ switch (iforce->bus) { #ifdef CONFIG_JOYSTICK_IFORCE_232 case IFORCE_232: if (empty) iforce_serial_xmit(iforce); break; #endif #ifdef CONFIG_JOYSTICK_IFORCE_USB case IFORCE_USB: if (iforce->usbdev && empty && !test_and_set_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags)) { iforce->xport_ops->xmit(iforce); iforce_usb_xmit(iforce); } break; #endif } return 0; } EXPORT_SYMBOL(iforce_send_packet); /* Start or stop an effect */ int iforce_control_playback(struct iforce* iforce, u16 id, unsigned int value) Loading Loading @@ -145,70 +131,74 @@ static int mark_core_as_ready(struct iforce *iforce, unsigned short addr) return -1; } void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data) static void iforce_report_hats_buttons(struct iforce *iforce, u8 *data) { struct input_dev *dev = iforce->dev; int i; static int being_used = 0; if (being_used) dev_warn(&iforce->dev->dev, "re-entrant call to iforce_process %d\n", being_used); being_used++; #ifdef CONFIG_JOYSTICK_IFORCE_232 if (HI(iforce->expect_packet) == HI(cmd)) { iforce->expect_packet = 0; iforce->ecmd = cmd; memcpy(iforce->edata, data, IFORCE_MAX_LENGTH); } #endif wake_up(&iforce->wait); if (!iforce->type) { being_used--; return; } switch (HI(cmd)) { case 0x01: /* joystick position data */ case 0x03: /* wheel position data */ if (HI(cmd) == 1) { input_report_abs(dev, ABS_X, (__s16) (((__s16)data[1] << 8) | data[0])); input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[3] << 8) | data[2])); input_report_abs(dev, ABS_THROTTLE, 255 - data[4]); if (LO(cmd) >= 8 && test_bit(ABS_RUDDER ,dev->absbit)) input_report_abs(dev, ABS_RUDDER, (__s8)data[7]); } else { input_report_abs(dev, ABS_WHEEL, (__s16) (((__s16)data[1] << 8) | data[0])); input_report_abs(dev, ABS_GAS, 255 - data[2]); input_report_abs(dev, ABS_BRAKE, 255 - data[3]); } input_report_abs(dev, ABS_HAT0X, iforce_hat_to_axis[data[6] >> 4].x); input_report_abs(dev, ABS_HAT0Y, iforce_hat_to_axis[data[6] >> 4].y); for (i = 0; iforce->type->btn[i] >= 0; i++) input_report_key(dev, iforce->type->btn[i], data[(i >> 3) + 5] & (1 << (i & 7))); input_report_key(dev, iforce->type->btn[i], data[(i >> 3) + 5] & (1 << (i & 7))); /* If there are untouched bits left, interpret them as the second hat */ if (i <= 8) { int btns = data[6]; u8 btns = data[6]; if (test_bit(ABS_HAT1X, dev->absbit)) { if (btns & 8) input_report_abs(dev, ABS_HAT1X, -1); else if (btns & 2) input_report_abs(dev, ABS_HAT1X, 1); else input_report_abs(dev, ABS_HAT1X, 0); if (btns & BIT(3)) input_report_abs(dev, ABS_HAT1X, -1); else if (btns & BIT(1)) input_report_abs(dev, ABS_HAT1X, 1); else input_report_abs(dev, ABS_HAT1X, 0); } if (test_bit(ABS_HAT1Y, dev->absbit)) { if (btns & 1) input_report_abs(dev, ABS_HAT1Y, -1); else if (btns & 4) input_report_abs(dev, ABS_HAT1Y, 1); else input_report_abs(dev, ABS_HAT1Y, 0); if (btns & BIT(0)) input_report_abs(dev, ABS_HAT1Y, -1); else if (btns & BIT(2)) input_report_abs(dev, ABS_HAT1Y, 1); else input_report_abs(dev, ABS_HAT1Y, 0); } } } void iforce_process_packet(struct iforce *iforce, u8 packet_id, u8 *data, size_t len) { struct input_dev *dev = iforce->dev; int i, j; switch (packet_id) { case 0x01: /* joystick position data */ input_report_abs(dev, ABS_X, (__s16) get_unaligned_le16(data)); input_report_abs(dev, ABS_Y, (__s16) get_unaligned_le16(data + 2)); input_report_abs(dev, ABS_THROTTLE, 255 - data[4]); if (len >= 8 && test_bit(ABS_RUDDER ,dev->absbit)) input_report_abs(dev, ABS_RUDDER, (__s8)data[7]); iforce_report_hats_buttons(iforce, data); input_sync(dev); break; case 0x03: /* wheel position data */ input_report_abs(dev, ABS_WHEEL, (__s16) get_unaligned_le16(data)); input_report_abs(dev, ABS_GAS, 255 - data[2]); input_report_abs(dev, ABS_BRAKE, 255 - data[3]); iforce_report_hats_buttons(iforce, data); input_sync(dev); break; case 0x02: /* status report */ Loading @@ -226,76 +216,11 @@ void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data) /* Report stop event */ input_report_ff_status(dev, i, FF_STATUS_STOPPED); } if (LO(cmd) > 3) { int j; for (j = 3; j < LO(cmd); j += 2) mark_core_as_ready(iforce, data[j] | (data[j+1]<<8)); } break; } being_used--; } int iforce_get_id_packet(struct iforce *iforce, char *packet) { switch (iforce->bus) { case IFORCE_USB: { #ifdef CONFIG_JOYSTICK_IFORCE_USB int status; iforce->cr.bRequest = packet[0]; iforce->ctrl->dev = iforce->usbdev; status = usb_submit_urb(iforce->ctrl, GFP_KERNEL); if (status) { dev_err(&iforce->intf->dev, "usb_submit_urb failed %d\n", status); return -1; } wait_event_interruptible_timeout(iforce->wait, iforce->ctrl->status != -EINPROGRESS, HZ); if (iforce->ctrl->status) { dev_dbg(&iforce->intf->dev, "iforce->ctrl->status = %d\n", iforce->ctrl->status); usb_unlink_urb(iforce->ctrl); return -1; } #else printk(KERN_DEBUG "iforce_get_id_packet: iforce->bus = USB!\n"); #endif } break; case IFORCE_232: for (j = 3; j < len; j += 2) mark_core_as_ready(iforce, get_unaligned_le16(data + j)); #ifdef CONFIG_JOYSTICK_IFORCE_232 iforce->expect_packet = FF_CMD_QUERY; iforce_send_packet(iforce, FF_CMD_QUERY, packet); wait_event_interruptible_timeout(iforce->wait, !iforce->expect_packet, HZ); if (iforce->expect_packet) { iforce->expect_packet = 0; return -1; } #else dev_err(&iforce->dev->dev, "iforce_get_id_packet: iforce->bus = SERIO!\n"); #endif break; default: dev_err(&iforce->dev->dev, "iforce_get_id_packet: iforce->bus = %d\n", iforce->bus); break; } return -(iforce->edata[0] != packet[0]); } EXPORT_SYMBOL(iforce_process_packet); drivers/input/joystick/iforce/iforce-serio.c +124 −37 Original line number Diff line number Diff line Loading @@ -21,10 +21,26 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/serio.h> #include "iforce.h" void iforce_serial_xmit(struct iforce *iforce) struct iforce_serio { struct iforce iforce; struct serio *serio; int idx, pkt, len, id; u8 csum; u8 expect_packet; u8 cmd_response[IFORCE_MAX_LENGTH]; u8 cmd_response_len; u8 data_in[IFORCE_MAX_LENGTH]; }; static void iforce_serio_xmit(struct iforce *iforce) { struct iforce_serio *iforce_serio = container_of(iforce, struct iforce_serio, iforce); unsigned char cs; int i; unsigned long flags; Loading @@ -45,19 +61,20 @@ void iforce_serial_xmit(struct iforce *iforce) cs = 0x2b; serio_write(iforce->serio, 0x2b); serio_write(iforce_serio->serio, 0x2b); serio_write(iforce->serio, iforce->xmit.buf[iforce->xmit.tail]); serio_write(iforce_serio->serio, iforce->xmit.buf[iforce->xmit.tail]); cs ^= iforce->xmit.buf[iforce->xmit.tail]; XMIT_INC(iforce->xmit.tail, 1); for (i=iforce->xmit.buf[iforce->xmit.tail]; i >= 0; --i) { serio_write(iforce->serio, iforce->xmit.buf[iforce->xmit.tail]); serio_write(iforce_serio->serio, iforce->xmit.buf[iforce->xmit.tail]); cs ^= iforce->xmit.buf[iforce->xmit.tail]; XMIT_INC(iforce->xmit.tail, 1); } serio_write(iforce->serio, cs); serio_write(iforce_serio->serio, cs); if (test_and_clear_bit(IFORCE_XMIT_AGAIN, iforce->xmit_flags)) goto again; Loading @@ -67,54 +84,118 @@ void iforce_serial_xmit(struct iforce *iforce) spin_unlock_irqrestore(&iforce->xmit_lock, flags); } static int iforce_serio_get_id(struct iforce *iforce, u8 id, u8 *response_data, size_t *response_len) { struct iforce_serio *iforce_serio = container_of(iforce, struct iforce_serio, iforce); iforce_serio->expect_packet = HI(FF_CMD_QUERY); iforce_serio->cmd_response_len = 0; iforce_send_packet(iforce, FF_CMD_QUERY, &id); wait_event_interruptible_timeout(iforce->wait, !iforce_serio->expect_packet, HZ); if (iforce_serio->expect_packet) { iforce_serio->expect_packet = 0; return -ETIMEDOUT; } if (iforce_serio->cmd_response[0] != id) return -EIO; memcpy(response_data, iforce_serio->cmd_response, iforce_serio->cmd_response_len); *response_len = iforce_serio->cmd_response_len; return 0; } static int iforce_serio_start_io(struct iforce *iforce) { /* No special handling required */ return 0; } static void iforce_serio_stop_io(struct iforce *iforce) { //TODO: Wait for the last packets to be sent } static const struct iforce_xport_ops iforce_serio_xport_ops = { .xmit = iforce_serio_xmit, .get_id = iforce_serio_get_id, .start_io = iforce_serio_start_io, .stop_io = iforce_serio_stop_io, }; static void iforce_serio_write_wakeup(struct serio *serio) { struct iforce *iforce = serio_get_drvdata(serio); iforce_serial_xmit(iforce); iforce_serio_xmit(iforce); } static irqreturn_t iforce_serio_irq(struct serio *serio, unsigned char data, unsigned int flags) { struct iforce *iforce = serio_get_drvdata(serio); struct iforce_serio *iforce_serio = serio_get_drvdata(serio); struct iforce *iforce = &iforce_serio->iforce; if (!iforce->pkt) { if (!iforce_serio->pkt) { if (data == 0x2b) iforce->pkt = 1; iforce_serio->pkt = 1; goto out; } if (!iforce->id) { if (!iforce_serio->id) { if (data > 3 && data != 0xff) iforce->pkt = 0; iforce_serio->pkt = 0; else iforce->id = data; iforce_serio->id = data; goto out; } if (!iforce->len) { if (!iforce_serio->len) { if (data > IFORCE_MAX_LENGTH) { iforce->pkt = 0; iforce->id = 0; iforce_serio->pkt = 0; iforce_serio->id = 0; } else { iforce->len = data; iforce_serio->len = data; } goto out; } if (iforce->idx < iforce->len) { iforce->csum += iforce->data[iforce->idx++] = data; if (iforce_serio->idx < iforce_serio->len) { iforce_serio->data_in[iforce_serio->idx++] = data; iforce_serio->csum += data; goto out; } if (iforce->idx == iforce->len) { iforce_process_packet(iforce, (iforce->id << 8) | iforce->idx, iforce->data); iforce->pkt = 0; iforce->id = 0; iforce->len = 0; iforce->idx = 0; iforce->csum = 0; if (iforce_serio->idx == iforce_serio->len) { /* Handle command completion */ if (iforce_serio->expect_packet == iforce_serio->id) { iforce_serio->expect_packet = 0; memcpy(iforce_serio->cmd_response, iforce_serio->data_in, IFORCE_MAX_LENGTH); iforce_serio->cmd_response_len = iforce_serio->len; /* Signal that command is done */ wake_up(&iforce->wait); } else if (likely(iforce->type)) { iforce_process_packet(iforce, iforce_serio->id, iforce_serio->data_in, iforce_serio->len); } iforce_serio->pkt = 0; iforce_serio->id = 0; iforce_serio->len = 0; iforce_serio->idx = 0; iforce_serio->csum = 0; } out: return IRQ_HANDLED; Loading @@ -122,23 +203,23 @@ static irqreturn_t iforce_serio_irq(struct serio *serio, static int iforce_serio_connect(struct serio *serio, struct serio_driver *drv) { struct iforce *iforce; struct iforce_serio *iforce_serio; int err; iforce = kzalloc(sizeof(struct iforce), GFP_KERNEL); if (!iforce) iforce_serio = kzalloc(sizeof(*iforce_serio), GFP_KERNEL); if (!iforce_serio) return -ENOMEM; iforce->bus = IFORCE_232; iforce->serio = serio; iforce_serio->iforce.xport_ops = &iforce_serio_xport_ops; serio_set_drvdata(serio, iforce); iforce_serio->serio = serio; serio_set_drvdata(serio, iforce_serio); err = serio_open(serio, drv); if (err) goto fail1; err = iforce_init_device(iforce); err = iforce_init_device(&serio->dev, BUS_RS232, &iforce_serio->iforce); if (err) goto fail2; Loading @@ -146,18 +227,18 @@ static int iforce_serio_connect(struct serio *serio, struct serio_driver *drv) fail2: serio_close(serio); fail1: serio_set_drvdata(serio, NULL); kfree(iforce); kfree(iforce_serio); return err; } static void iforce_serio_disconnect(struct serio *serio) { struct iforce *iforce = serio_get_drvdata(serio); struct iforce_serio *iforce_serio = serio_get_drvdata(serio); input_unregister_device(iforce->dev); input_unregister_device(iforce_serio->iforce.dev); serio_close(serio); serio_set_drvdata(serio, NULL); kfree(iforce); kfree(iforce_serio); } static const struct serio_device_id iforce_serio_ids[] = { Loading @@ -183,3 +264,9 @@ struct serio_driver iforce_serio_drv = { .connect = iforce_serio_connect, .disconnect = iforce_serio_disconnect, }; module_serio_driver(iforce_serio_drv); MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>, Johann Deneux <johann.deneux@gmail.com>"); MODULE_DESCRIPTION("RS232 I-Force joysticks and wheels driver"); MODULE_LICENSE("GPL"); Loading
drivers/input/joystick/iforce/Kconfig +4 −4 Original line number Diff line number Diff line Loading @@ -13,15 +13,15 @@ config JOYSTICK_IFORCE module will be called iforce. config JOYSTICK_IFORCE_USB bool "I-Force USB joysticks and wheels" depends on JOYSTICK_IFORCE && (JOYSTICK_IFORCE=m || USB=y) && USB tristate "I-Force USB joysticks and wheels" depends on JOYSTICK_IFORCE && USB help Say Y here if you have an I-Force joystick or steering wheel connected to your USB port. config JOYSTICK_IFORCE_232 bool "I-Force Serial joysticks and wheels" depends on JOYSTICK_IFORCE && (JOYSTICK_IFORCE=m || SERIO=y) && SERIO tristate "I-Force Serial joysticks and wheels" depends on JOYSTICK_IFORCE && SERIO help Say Y here if you have an I-Force joystick or steering wheel connected to your serial (COM) port. Loading
drivers/input/joystick/iforce/Makefile +3 −4 Original line number Diff line number Diff line Loading @@ -5,7 +5,6 @@ # obj-$(CONFIG_JOYSTICK_IFORCE) += iforce.o iforce-y := iforce-ff.o iforce-main.o iforce-packets.o iforce-$(CONFIG_JOYSTICK_IFORCE_232) += iforce-serio.o iforce-$(CONFIG_JOYSTICK_IFORCE_USB) += iforce-usb.o obj-$(CONFIG_JOYSTICK_IFORCE_232) += iforce-serio.o obj-$(CONFIG_JOYSTICK_IFORCE_USB) += iforce-usb.o
drivers/input/joystick/iforce/iforce-main.c +57 −121 Original line number Diff line number Diff line Loading @@ -21,10 +21,11 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <asm/unaligned.h> #include "iforce.h" MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>, Johann Deneux <johann.deneux@gmail.com>"); MODULE_DESCRIPTION("USB/RS232 I-Force joysticks and wheels driver"); MODULE_DESCRIPTION("Core I-Force joysticks and wheels driver"); MODULE_LICENSE("GPL"); static signed short btn_joystick[] = Loading Loading @@ -67,6 +68,7 @@ static struct iforce_device iforce_device[] = { { 0x05ef, 0x8888, "AVB Top Shot Force Feedback Racing Wheel", btn_wheel, abs_wheel, ff_iforce }, //? { 0x061c, 0xc0a4, "ACT LABS Force RS", btn_wheel, abs_wheel, ff_iforce }, //? { 0x061c, 0xc084, "ACT LABS Force RS", btn_wheel, abs_wheel, ff_iforce }, { 0x06a3, 0xff04, "Saitek R440 Force Wheel", btn_wheel, abs_wheel, ff_iforce }, //? { 0x06f8, 0x0001, "Guillemot Race Leader Force Feedback", btn_wheel, abs_wheel, ff_iforce }, //? { 0x06f8, 0x0001, "Guillemot Jet Leader Force Feedback", btn_joystick, abs_joystick_rudder, ff_iforce }, { 0x06f8, 0x0004, "Guillemot Force Feedback Racing Wheel", btn_wheel, abs_wheel, ff_iforce }, //? Loading Loading @@ -132,7 +134,6 @@ static int iforce_upload_effect(struct input_dev *dev, struct ff_effect *effect, * Upload the effect */ switch (effect->type) { case FF_PERIODIC: ret = iforce_upload_periodic(iforce, effect, old); break; Loading Loading @@ -185,15 +186,7 @@ static int iforce_open(struct input_dev *dev) { struct iforce *iforce = input_get_drvdata(dev); switch (iforce->bus) { #ifdef CONFIG_JOYSTICK_IFORCE_USB case IFORCE_USB: iforce->irq->dev = iforce->usbdev; if (usb_submit_urb(iforce->irq, GFP_KERNEL)) return -EIO; break; #endif } iforce->xport_ops->start_io(iforce); if (test_bit(EV_FF, dev->evbit)) { /* Enable force feedback */ Loading Loading @@ -226,27 +219,17 @@ static void iforce_close(struct input_dev *dev) !test_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags)); } switch (iforce->bus) { #ifdef CONFIG_JOYSTICK_IFORCE_USB case IFORCE_USB: usb_kill_urb(iforce->irq); usb_kill_urb(iforce->out); usb_kill_urb(iforce->ctrl); break; #endif #ifdef CONFIG_JOYSTICK_IFORCE_232 case IFORCE_232: //TODO: Wait for the last packets to be sent break; #endif } iforce->xport_ops->stop_io(iforce); } int iforce_init_device(struct iforce *iforce) int iforce_init_device(struct device *parent, u16 bustype, struct iforce *iforce) { struct input_dev *input_dev; struct ff_device *ff; unsigned char c[] = "CEOV"; u8 c[] = "CEOV"; u8 buf[IFORCE_MAX_LENGTH]; size_t len; int i, error; int ff_effects = 0; Loading @@ -264,20 +247,8 @@ int iforce_init_device(struct iforce *iforce) * Input device fields. */ switch (iforce->bus) { #ifdef CONFIG_JOYSTICK_IFORCE_USB case IFORCE_USB: input_dev->id.bustype = BUS_USB; input_dev->dev.parent = &iforce->usbdev->dev; break; #endif #ifdef CONFIG_JOYSTICK_IFORCE_232 case IFORCE_232: input_dev->id.bustype = BUS_RS232; input_dev->dev.parent = &iforce->serio->dev; break; #endif } input_dev->id.bustype = bustype; input_dev->dev.parent = parent; input_set_drvdata(input_dev, iforce); Loading @@ -302,7 +273,7 @@ int iforce_init_device(struct iforce *iforce) */ for (i = 0; i < 20; i++) if (!iforce_get_id_packet(iforce, "O")) if (!iforce_get_id_packet(iforce, 'O', buf, &len)) break; if (i == 20) { /* 5 seconds */ Loading @@ -316,23 +287,23 @@ int iforce_init_device(struct iforce *iforce) * Get device info. */ if (!iforce_get_id_packet(iforce, "M")) input_dev->id.vendor = (iforce->edata[2] << 8) | iforce->edata[1]; if (!iforce_get_id_packet(iforce, 'M', buf, &len) || len < 3) input_dev->id.vendor = get_unaligned_le16(buf + 1); else dev_warn(&iforce->dev->dev, "Device does not respond to id packet M\n"); if (!iforce_get_id_packet(iforce, "P")) input_dev->id.product = (iforce->edata[2] << 8) | iforce->edata[1]; if (!iforce_get_id_packet(iforce, 'P', buf, &len) || len < 3) input_dev->id.product = get_unaligned_le16(buf + 1); else dev_warn(&iforce->dev->dev, "Device does not respond to id packet P\n"); if (!iforce_get_id_packet(iforce, "B")) iforce->device_memory.end = (iforce->edata[2] << 8) | iforce->edata[1]; if (!iforce_get_id_packet(iforce, 'B', buf, &len) || len < 3) iforce->device_memory.end = get_unaligned_le16(buf + 1); else dev_warn(&iforce->dev->dev, "Device does not respond to id packet B\n"); if (!iforce_get_id_packet(iforce, "N")) ff_effects = iforce->edata[1]; if (!iforce_get_id_packet(iforce, 'N', buf, &len) || len < 2) ff_effects = buf[1]; else dev_warn(&iforce->dev->dev, "Device does not respond to id packet N\n"); Loading @@ -348,8 +319,9 @@ int iforce_init_device(struct iforce *iforce) */ for (i = 0; c[i]; i++) if (!iforce_get_id_packet(iforce, c + i)) iforce_dump_packet(iforce, "info", iforce->ecmd, iforce->edata); if (!iforce_get_id_packet(iforce, c[i], buf, &len)) iforce_dump_packet(iforce, "info", (FF_CMD_QUERY & 0xff00) | len, buf); /* * Disable spring, enable force feedback. Loading Loading @@ -383,11 +355,9 @@ int iforce_init_device(struct iforce *iforce) signed short t = iforce->type->abs[i]; switch (t) { case ABS_X: case ABS_Y: case ABS_WHEEL: input_set_abs_params(input_dev, t, -1920, 1920, 16, 128); set_bit(t, input_dev->ffbit); break; Loading @@ -395,12 +365,10 @@ int iforce_init_device(struct iforce *iforce) case ABS_THROTTLE: case ABS_GAS: case ABS_BRAKE: input_set_abs_params(input_dev, t, 0, 255, 0, 0); break; case ABS_RUDDER: input_set_abs_params(input_dev, t, -128, 127, 0, 0); break; Loading @@ -408,7 +376,6 @@ int iforce_init_device(struct iforce *iforce) case ABS_HAT0Y: case ABS_HAT1X: case ABS_HAT1Y: input_set_abs_params(input_dev, t, -1, 1, 0, 0); break; } Loading Loading @@ -443,35 +410,4 @@ int iforce_init_device(struct iforce *iforce) fail: input_free_device(input_dev); return error; } static int __init iforce_init(void) { int err = 0; #ifdef CONFIG_JOYSTICK_IFORCE_USB err = usb_register(&iforce_usb_driver); if (err) return err; #endif #ifdef CONFIG_JOYSTICK_IFORCE_232 err = serio_register_driver(&iforce_serio_drv); #ifdef CONFIG_JOYSTICK_IFORCE_USB if (err) usb_deregister(&iforce_usb_driver); #endif #endif return err; } static void __exit iforce_exit(void) { #ifdef CONFIG_JOYSTICK_IFORCE_USB usb_deregister(&iforce_usb_driver); #endif #ifdef CONFIG_JOYSTICK_IFORCE_232 serio_unregister_driver(&iforce_serio_drv); #endif } module_init(iforce_init); module_exit(iforce_exit); EXPORT_SYMBOL(iforce_init_device);
drivers/input/joystick/iforce/iforce-packets.c +70 −145 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <asm/unaligned.h> #include "iforce.h" static struct { Loading Loading @@ -91,27 +92,12 @@ int iforce_send_packet(struct iforce *iforce, u16 cmd, unsigned char* data) /* * If necessary, start the transmission */ switch (iforce->bus) { #ifdef CONFIG_JOYSTICK_IFORCE_232 case IFORCE_232: if (empty) iforce_serial_xmit(iforce); break; #endif #ifdef CONFIG_JOYSTICK_IFORCE_USB case IFORCE_USB: if (iforce->usbdev && empty && !test_and_set_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags)) { iforce->xport_ops->xmit(iforce); iforce_usb_xmit(iforce); } break; #endif } return 0; } EXPORT_SYMBOL(iforce_send_packet); /* Start or stop an effect */ int iforce_control_playback(struct iforce* iforce, u16 id, unsigned int value) Loading Loading @@ -145,70 +131,74 @@ static int mark_core_as_ready(struct iforce *iforce, unsigned short addr) return -1; } void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data) static void iforce_report_hats_buttons(struct iforce *iforce, u8 *data) { struct input_dev *dev = iforce->dev; int i; static int being_used = 0; if (being_used) dev_warn(&iforce->dev->dev, "re-entrant call to iforce_process %d\n", being_used); being_used++; #ifdef CONFIG_JOYSTICK_IFORCE_232 if (HI(iforce->expect_packet) == HI(cmd)) { iforce->expect_packet = 0; iforce->ecmd = cmd; memcpy(iforce->edata, data, IFORCE_MAX_LENGTH); } #endif wake_up(&iforce->wait); if (!iforce->type) { being_used--; return; } switch (HI(cmd)) { case 0x01: /* joystick position data */ case 0x03: /* wheel position data */ if (HI(cmd) == 1) { input_report_abs(dev, ABS_X, (__s16) (((__s16)data[1] << 8) | data[0])); input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[3] << 8) | data[2])); input_report_abs(dev, ABS_THROTTLE, 255 - data[4]); if (LO(cmd) >= 8 && test_bit(ABS_RUDDER ,dev->absbit)) input_report_abs(dev, ABS_RUDDER, (__s8)data[7]); } else { input_report_abs(dev, ABS_WHEEL, (__s16) (((__s16)data[1] << 8) | data[0])); input_report_abs(dev, ABS_GAS, 255 - data[2]); input_report_abs(dev, ABS_BRAKE, 255 - data[3]); } input_report_abs(dev, ABS_HAT0X, iforce_hat_to_axis[data[6] >> 4].x); input_report_abs(dev, ABS_HAT0Y, iforce_hat_to_axis[data[6] >> 4].y); for (i = 0; iforce->type->btn[i] >= 0; i++) input_report_key(dev, iforce->type->btn[i], data[(i >> 3) + 5] & (1 << (i & 7))); input_report_key(dev, iforce->type->btn[i], data[(i >> 3) + 5] & (1 << (i & 7))); /* If there are untouched bits left, interpret them as the second hat */ if (i <= 8) { int btns = data[6]; u8 btns = data[6]; if (test_bit(ABS_HAT1X, dev->absbit)) { if (btns & 8) input_report_abs(dev, ABS_HAT1X, -1); else if (btns & 2) input_report_abs(dev, ABS_HAT1X, 1); else input_report_abs(dev, ABS_HAT1X, 0); if (btns & BIT(3)) input_report_abs(dev, ABS_HAT1X, -1); else if (btns & BIT(1)) input_report_abs(dev, ABS_HAT1X, 1); else input_report_abs(dev, ABS_HAT1X, 0); } if (test_bit(ABS_HAT1Y, dev->absbit)) { if (btns & 1) input_report_abs(dev, ABS_HAT1Y, -1); else if (btns & 4) input_report_abs(dev, ABS_HAT1Y, 1); else input_report_abs(dev, ABS_HAT1Y, 0); if (btns & BIT(0)) input_report_abs(dev, ABS_HAT1Y, -1); else if (btns & BIT(2)) input_report_abs(dev, ABS_HAT1Y, 1); else input_report_abs(dev, ABS_HAT1Y, 0); } } } void iforce_process_packet(struct iforce *iforce, u8 packet_id, u8 *data, size_t len) { struct input_dev *dev = iforce->dev; int i, j; switch (packet_id) { case 0x01: /* joystick position data */ input_report_abs(dev, ABS_X, (__s16) get_unaligned_le16(data)); input_report_abs(dev, ABS_Y, (__s16) get_unaligned_le16(data + 2)); input_report_abs(dev, ABS_THROTTLE, 255 - data[4]); if (len >= 8 && test_bit(ABS_RUDDER ,dev->absbit)) input_report_abs(dev, ABS_RUDDER, (__s8)data[7]); iforce_report_hats_buttons(iforce, data); input_sync(dev); break; case 0x03: /* wheel position data */ input_report_abs(dev, ABS_WHEEL, (__s16) get_unaligned_le16(data)); input_report_abs(dev, ABS_GAS, 255 - data[2]); input_report_abs(dev, ABS_BRAKE, 255 - data[3]); iforce_report_hats_buttons(iforce, data); input_sync(dev); break; case 0x02: /* status report */ Loading @@ -226,76 +216,11 @@ void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data) /* Report stop event */ input_report_ff_status(dev, i, FF_STATUS_STOPPED); } if (LO(cmd) > 3) { int j; for (j = 3; j < LO(cmd); j += 2) mark_core_as_ready(iforce, data[j] | (data[j+1]<<8)); } break; } being_used--; } int iforce_get_id_packet(struct iforce *iforce, char *packet) { switch (iforce->bus) { case IFORCE_USB: { #ifdef CONFIG_JOYSTICK_IFORCE_USB int status; iforce->cr.bRequest = packet[0]; iforce->ctrl->dev = iforce->usbdev; status = usb_submit_urb(iforce->ctrl, GFP_KERNEL); if (status) { dev_err(&iforce->intf->dev, "usb_submit_urb failed %d\n", status); return -1; } wait_event_interruptible_timeout(iforce->wait, iforce->ctrl->status != -EINPROGRESS, HZ); if (iforce->ctrl->status) { dev_dbg(&iforce->intf->dev, "iforce->ctrl->status = %d\n", iforce->ctrl->status); usb_unlink_urb(iforce->ctrl); return -1; } #else printk(KERN_DEBUG "iforce_get_id_packet: iforce->bus = USB!\n"); #endif } break; case IFORCE_232: for (j = 3; j < len; j += 2) mark_core_as_ready(iforce, get_unaligned_le16(data + j)); #ifdef CONFIG_JOYSTICK_IFORCE_232 iforce->expect_packet = FF_CMD_QUERY; iforce_send_packet(iforce, FF_CMD_QUERY, packet); wait_event_interruptible_timeout(iforce->wait, !iforce->expect_packet, HZ); if (iforce->expect_packet) { iforce->expect_packet = 0; return -1; } #else dev_err(&iforce->dev->dev, "iforce_get_id_packet: iforce->bus = SERIO!\n"); #endif break; default: dev_err(&iforce->dev->dev, "iforce_get_id_packet: iforce->bus = %d\n", iforce->bus); break; } return -(iforce->edata[0] != packet[0]); } EXPORT_SYMBOL(iforce_process_packet);
drivers/input/joystick/iforce/iforce-serio.c +124 −37 Original line number Diff line number Diff line Loading @@ -21,10 +21,26 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/serio.h> #include "iforce.h" void iforce_serial_xmit(struct iforce *iforce) struct iforce_serio { struct iforce iforce; struct serio *serio; int idx, pkt, len, id; u8 csum; u8 expect_packet; u8 cmd_response[IFORCE_MAX_LENGTH]; u8 cmd_response_len; u8 data_in[IFORCE_MAX_LENGTH]; }; static void iforce_serio_xmit(struct iforce *iforce) { struct iforce_serio *iforce_serio = container_of(iforce, struct iforce_serio, iforce); unsigned char cs; int i; unsigned long flags; Loading @@ -45,19 +61,20 @@ void iforce_serial_xmit(struct iforce *iforce) cs = 0x2b; serio_write(iforce->serio, 0x2b); serio_write(iforce_serio->serio, 0x2b); serio_write(iforce->serio, iforce->xmit.buf[iforce->xmit.tail]); serio_write(iforce_serio->serio, iforce->xmit.buf[iforce->xmit.tail]); cs ^= iforce->xmit.buf[iforce->xmit.tail]; XMIT_INC(iforce->xmit.tail, 1); for (i=iforce->xmit.buf[iforce->xmit.tail]; i >= 0; --i) { serio_write(iforce->serio, iforce->xmit.buf[iforce->xmit.tail]); serio_write(iforce_serio->serio, iforce->xmit.buf[iforce->xmit.tail]); cs ^= iforce->xmit.buf[iforce->xmit.tail]; XMIT_INC(iforce->xmit.tail, 1); } serio_write(iforce->serio, cs); serio_write(iforce_serio->serio, cs); if (test_and_clear_bit(IFORCE_XMIT_AGAIN, iforce->xmit_flags)) goto again; Loading @@ -67,54 +84,118 @@ void iforce_serial_xmit(struct iforce *iforce) spin_unlock_irqrestore(&iforce->xmit_lock, flags); } static int iforce_serio_get_id(struct iforce *iforce, u8 id, u8 *response_data, size_t *response_len) { struct iforce_serio *iforce_serio = container_of(iforce, struct iforce_serio, iforce); iforce_serio->expect_packet = HI(FF_CMD_QUERY); iforce_serio->cmd_response_len = 0; iforce_send_packet(iforce, FF_CMD_QUERY, &id); wait_event_interruptible_timeout(iforce->wait, !iforce_serio->expect_packet, HZ); if (iforce_serio->expect_packet) { iforce_serio->expect_packet = 0; return -ETIMEDOUT; } if (iforce_serio->cmd_response[0] != id) return -EIO; memcpy(response_data, iforce_serio->cmd_response, iforce_serio->cmd_response_len); *response_len = iforce_serio->cmd_response_len; return 0; } static int iforce_serio_start_io(struct iforce *iforce) { /* No special handling required */ return 0; } static void iforce_serio_stop_io(struct iforce *iforce) { //TODO: Wait for the last packets to be sent } static const struct iforce_xport_ops iforce_serio_xport_ops = { .xmit = iforce_serio_xmit, .get_id = iforce_serio_get_id, .start_io = iforce_serio_start_io, .stop_io = iforce_serio_stop_io, }; static void iforce_serio_write_wakeup(struct serio *serio) { struct iforce *iforce = serio_get_drvdata(serio); iforce_serial_xmit(iforce); iforce_serio_xmit(iforce); } static irqreturn_t iforce_serio_irq(struct serio *serio, unsigned char data, unsigned int flags) { struct iforce *iforce = serio_get_drvdata(serio); struct iforce_serio *iforce_serio = serio_get_drvdata(serio); struct iforce *iforce = &iforce_serio->iforce; if (!iforce->pkt) { if (!iforce_serio->pkt) { if (data == 0x2b) iforce->pkt = 1; iforce_serio->pkt = 1; goto out; } if (!iforce->id) { if (!iforce_serio->id) { if (data > 3 && data != 0xff) iforce->pkt = 0; iforce_serio->pkt = 0; else iforce->id = data; iforce_serio->id = data; goto out; } if (!iforce->len) { if (!iforce_serio->len) { if (data > IFORCE_MAX_LENGTH) { iforce->pkt = 0; iforce->id = 0; iforce_serio->pkt = 0; iforce_serio->id = 0; } else { iforce->len = data; iforce_serio->len = data; } goto out; } if (iforce->idx < iforce->len) { iforce->csum += iforce->data[iforce->idx++] = data; if (iforce_serio->idx < iforce_serio->len) { iforce_serio->data_in[iforce_serio->idx++] = data; iforce_serio->csum += data; goto out; } if (iforce->idx == iforce->len) { iforce_process_packet(iforce, (iforce->id << 8) | iforce->idx, iforce->data); iforce->pkt = 0; iforce->id = 0; iforce->len = 0; iforce->idx = 0; iforce->csum = 0; if (iforce_serio->idx == iforce_serio->len) { /* Handle command completion */ if (iforce_serio->expect_packet == iforce_serio->id) { iforce_serio->expect_packet = 0; memcpy(iforce_serio->cmd_response, iforce_serio->data_in, IFORCE_MAX_LENGTH); iforce_serio->cmd_response_len = iforce_serio->len; /* Signal that command is done */ wake_up(&iforce->wait); } else if (likely(iforce->type)) { iforce_process_packet(iforce, iforce_serio->id, iforce_serio->data_in, iforce_serio->len); } iforce_serio->pkt = 0; iforce_serio->id = 0; iforce_serio->len = 0; iforce_serio->idx = 0; iforce_serio->csum = 0; } out: return IRQ_HANDLED; Loading @@ -122,23 +203,23 @@ static irqreturn_t iforce_serio_irq(struct serio *serio, static int iforce_serio_connect(struct serio *serio, struct serio_driver *drv) { struct iforce *iforce; struct iforce_serio *iforce_serio; int err; iforce = kzalloc(sizeof(struct iforce), GFP_KERNEL); if (!iforce) iforce_serio = kzalloc(sizeof(*iforce_serio), GFP_KERNEL); if (!iforce_serio) return -ENOMEM; iforce->bus = IFORCE_232; iforce->serio = serio; iforce_serio->iforce.xport_ops = &iforce_serio_xport_ops; serio_set_drvdata(serio, iforce); iforce_serio->serio = serio; serio_set_drvdata(serio, iforce_serio); err = serio_open(serio, drv); if (err) goto fail1; err = iforce_init_device(iforce); err = iforce_init_device(&serio->dev, BUS_RS232, &iforce_serio->iforce); if (err) goto fail2; Loading @@ -146,18 +227,18 @@ static int iforce_serio_connect(struct serio *serio, struct serio_driver *drv) fail2: serio_close(serio); fail1: serio_set_drvdata(serio, NULL); kfree(iforce); kfree(iforce_serio); return err; } static void iforce_serio_disconnect(struct serio *serio) { struct iforce *iforce = serio_get_drvdata(serio); struct iforce_serio *iforce_serio = serio_get_drvdata(serio); input_unregister_device(iforce->dev); input_unregister_device(iforce_serio->iforce.dev); serio_close(serio); serio_set_drvdata(serio, NULL); kfree(iforce); kfree(iforce_serio); } static const struct serio_device_id iforce_serio_ids[] = { Loading @@ -183,3 +264,9 @@ struct serio_driver iforce_serio_drv = { .connect = iforce_serio_connect, .disconnect = iforce_serio_disconnect, }; module_serio_driver(iforce_serio_drv); MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>, Johann Deneux <johann.deneux@gmail.com>"); MODULE_DESCRIPTION("RS232 I-Force joysticks and wheels driver"); MODULE_LICENSE("GPL");