Hi Peta, On Monday 16 March 2009 19:21:18 Peta Blaha wrote: > Driver for Fortius 1942 device. > Thank you very much for your patch, overall it looks pretty nice. It looks like your mailer ate whitespace; I also have some requests for you with regard to the driver. > Signed-off-by: Petr Blaha <blahapeta@xxxxxxxxx> Did you misspell your first name in the signed-off-by by any chance? > --- > > --- a/drivers/input/joystick/fortius_1942.c.orig 1970-01-01 > 01:00:00.000000000 +0100 > +++ b/drivers/input/joystick/fortius_1942.c 2009-03-17 03:17:01.000000000 > +0100 @@ -0,0 +1,394 @@ > +/* > + * 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. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA > + * > + * > + * > + * Info. > + * First unimpemented feature Heart Sensor. > + * Use bit 12 and 14(beginning 0), maybe 13 also.Emmisions > + * from computer affect measuring. > + * > + * Second is Active Brake > + * Can be maybe used as force feedback. Problem is slowness in > + * switching from state to state, its electric motor, it has delay can > + * be fragile. > + * Try it by changing BRAKE_POS from 0 to 10. > + * > + * Thirth unimpemented is sending 0x01, when pedal magnet meets frame > sensor. + * > + * Pedalling works fine. Your wheel can have other size tan mime. Feel > free + * to change constant in usb_tfor_irq, when counting value of > gasolina. + * > + */ > + > +#include <linux/kernel.h> > +#include <linux/slab.h> > +#include <linux/module.h> > +#include <linux/init.h> > +#include <linux/usb/input.h> > +#include <linux/time.h> > + > + > +#define DRIVER_VERSION "v0.0.2" > +#define DRIVER_DESC "USB Tacx Fortius 1942 driver" > +#define DRIVER_LICENSE "GPL" > +#define DRIVER_AUTHOR "Petr Blaha <blahapeta@xxxxxxxxx>" > + > + > +/*Don't put here lesser values*/ > +/*read*/ > +#define BIT_IN_DEVICE_1942 64 > +/*write*/ > +#define BIT_OUT_DEVICE_1942 12 > +/*read*/ > +#define BULK_IN_INTERFACE_1942 82 > +/*write*/ > +#define BULK_OUT_INTERFACE_1942 02 > + > + > +#define BRAKE_POS 2 > + > + > +/*11 states of brake,in beginning active brake helps, later add > +difficulty to cycling.0x00 0x00 is neutral*/ > +#define ARRAYINT { {0x4d, 0xf3}, {0xa7, 0xf9}, {0x00, 0x00}, {0x59, 0x06}, > \ +{0xb3, 0x0c}, {0x0c, 0x13}, {0x66, 0x19}, {0xbf, 0x1f}, {0x18, 0x26}, > {0x72, \ +0x2c}, {0xcb, 0x32} } > + > + > +MODULE_AUTHOR(DRIVER_AUTHOR); > +MODULE_DESCRIPTION(DRIVER_DESC); > +MODULE_LICENSE(DRIVER_LICENSE); > + > + unsigned long difference = 0; Make it static? Also there is no need to initialize to 0. > + /*time measure*/ > + struct timeval time, time_before; > + /*guessed fastest speed a man can possibly ride-incitating full > + throttle*/ > + unsigned int fastest_wheel_rotate = 1000; > + /*actual state of path reached in one cycle*/ > + unsigned int ride = 0, old_ride = 0; Same here. You really want to mark everything static unless it is used from another module. > + > +struct usb_tfor { > + char name[128]; > + char phys[64]; > + struct usb_device *usbdev; > + struct input_dev *input; > + > + struct urb *irq; > + unsigned char *data; > + dma_addr_t idata_dma; > + > + struct urb *out; > + unsigned char *data_device; > + dma_addr_t odata_dma; > + /*brake position*/ > + unsigned int brake_pos; > +}; > + > +static void tfor_disconnect(struct usb_interface *intf) Mark it as __devexit please. > +{ > + struct usb_tfor *tfor = usb_get_intfdata(intf); > + > + usb_set_intfdata(intf, NULL); > + if (tfor) { Why do we check this? Can it ever be NULL and still tfor_disconnect be called for this interface? > + /*urb*/ > + usb_kill_urb(tfor->out); > + usb_kill_urb(tfor->irq); You have implemented close method which is guaranteed to be called if open succeeded. These is no need to kill URBs here. > + /*device*/ > + input_unregister_device(tfor->input); > + /*urb*/ > + usb_free_urb(tfor->out); > + usb_free_urb(tfor->irq); > + /*buffer free*/ > + usb_buffer_free(interface_to_usbdev(intf), 10, tfor->data_device, \ > + tfor->odata_dma); > + usb_buffer_free(interface_to_usbdev(intf), 10, tfor->data, \ > + tfor->idata_dma); > + /*free memory*/ > + kfree(tfor); > + } > +} > + > +static int usb_tfor_open(struct input_dev *dev) > +{ > + struct usb_tfor *tfor = input_get_drvdata(dev); Blank line after variable declarations please. > + tfor->irq->dev = tfor->usbdev; This kind of setup should be moved into _probe(). > + usb_submit_urb(tfor->irq, GFP_KERNEL); > + tfor->out->dev = tfor->usbdev; This one as well, so the only thing left in _open should be calls to usb_submit_urb(). > + usb_submit_urb(tfor->out, GFP_KERNEL); > + > + return 0; > +} > + > +static void usb_tfor_close(struct input_dev *dev) > +{ > + struct usb_tfor *tfor = input_get_drvdata(dev); > + usb_kill_urb(tfor->irq); > + usb_kill_urb(tfor->out); > +} > + > +static struct usb_device_id tfor_ids[] = { > + { USB_DEVICE(0x3561, 0x1942), .driver_info = 0 }, > + { } > +}; > + > +static void usb_tfor_out(struct urb *urb) > +{ struct usb_tfor *tfor = urb->context; > + unsigned int brake_bits[11][2] = ARRAYINT; > + int retval; > + tfor->brake_pos = BRAKE_POS; > + /*URB to device*/ > + /*constant*/ > + tfor->data_device[0] = 0x01; > + /*constant*/ > + tfor->data_device[1] = 0x08; > + /*constant*/ > + tfor->data_device[2] = 0x01; > + /*constant*/ > + tfor->data_device[3] = 0x00; > + /*brake_bits*/ > + tfor->data_device[4] = brake_bits[tfor->brake_pos][0]; > + /*brake_bits*/ > + tfor->data_device[5] = brake_bits[tfor->brake_pos][1]; > + /*0x01 is turn,when frame sensor meets pedal magnet, > + i don't see reason for implementation, nor know how device knows it*/ > + tfor->data_device[6] = 0x00; > + /*constant*/ > + tfor->data_device[7] = 0x00; > + /*constant*/ > + tfor->data_device[8] = 0x02; > + /*constant*/ > + tfor->data_device[9] = 0x52; > + /*constant*/ > + tfor->data_device[10] = 0x10; > + /*constant*/ > + tfor->data_device[11] = 0x04; > + > + > + switch (urb->status) { > + case 0: > + /* success */ > + break; > + case -ECONNRESET: > + case -ENOENT: > + case -ESHUTDOWN: > + /* this urb is terminated, clean up */ > + dbg("%s - urb shutting down with status: %d", __func__, urb->status); > + break; > + default: > + dbg("%s - nonzero urb status received: %d", __func__, urb->status); > + } > + retval = usb_submit_urb(urb, GFP_ATOMIC); > + if (retval) > + err("%s - usb_submit_urb failed with result %d", > + __func__, retval); > +} > + > +static void usb_tfor_irq(struct urb *urb) > +{ > + struct usb_tfor *tfor = urb->context; > + unsigned char *data = tfor->data; > + struct input_dev *dev = tfor->input; > + int status; > + int gasolina; > + > + switch (urb->status) { > + case 0: > + /* success */ > + break; > + case -ECONNRESET: > + case -ENOENT: > + case -ESHUTDOWN: > + /* ±this urb is terminated, clean up */ > + dbg("%s - urb shutting down with status: %d", __func__, \ > + urb->status); > + return; > + default: > + dbg("%s - nonzero urb status received: %d", __func__, \ > + urb->status); > + goto resubmit; > + } > + > + /*Get actual time*/ > + do_gettimeofday(&time); > + /*Ride counter in velociped is small,avoid mistake here*/ > + if ((data[28]*255+data[29]) < ride) > + ride = 0; > + /*We get new data, set new val. of ride counter &time counter*/ > + if ((data[28]*255+data[29]) > ride) { > + old_ride = ride; > + ride = (data[28]*255+data[29]); > + difference = \ > + ((time.tv_usec-time_before.tv_usec)\ > + +(time.tv_sec-time_before.tv_sec)*1000000); > + do_gettimeofday(&time_before); > + if (difference == 0) > + dbg("Something is not good \n"); > + } > + /*Prevent division by 0*/ > + if (difference != 0) > + /*"speed=path/time",change constant 1000 > + to naturalize it for your speed*/ > + gasolina = (ride-old_ride)/(difference/1000); > + else > + gasolina = 0; We should probably rename this variable to accel[eration]. > + input_report_abs(dev, ABS_GAS, gasolina); > + input_report_key(dev, BTN_RIGHT, data[13] & 0x01); > + input_report_key(dev, BTN_BACK, data[13] & 0x02); > + input_report_key(dev, BTN_FORWARD, data[13] & 0x04); > + input_report_key(dev, BTN_LEFT, data[13] & 0x08); > + input_report_abs(dev, ABS_WHEEL, (((data[18]+((data[19] & 0x0f)*225))-\ > + (689+170))*-1)); > + > + /* event termination */ > + input_sync(dev); > + > +resubmit: > + status = usb_submit_urb(urb, GFP_ATOMIC); > + if (status) > + err("can't resubmit intr, %s-%s/input0, status %d", > + tfor->usbdev->bus->bus_name, tfor->usbdev->devpath, status); We should not use bus_name directly, i think there is a helper in driver core for that. Or you can use dev->phys instead. > + > +} > + > +static int tfor_probe(struct usb_interface *intf, const struct > usb_device_id *id) I think we can mark it __devinit. > +{ > + struct usb_device *dev = interface_to_usbdev(intf); > + struct usb_tfor *tfor; > + struct input_dev *input_dev; > + unsigned int err = -ENOMEM; > + > + > + tfor = kzalloc(sizeof(struct usb_tfor), GFP_KERNEL); > + > + input_dev = input_allocate_device(); > + if (!tfor || !input_dev) { > + err = -ENOMEM; > + goto fail1; > + } > + /*initilization of time counter*/ > + do_gettimeofday(&time); > + time_before.tv_usec = time.tv_usec; > + time_before.tv_sec = time.tv_sec; > + > + tfor->data = usb_buffer_alloc(dev, BIT_IN_DEVICE_1942, GFP_KERNEL, \ > + &tfor->idata_dma); > + if (!tfor->data) { > + err = -ENOMEM; > + goto fail1; > + } > + > + tfor->data_device = usb_buffer_alloc(dev, BIT_OUT_DEVICE_1942, \ > + GFP_KERNEL, &tfor->odata_dma); > + if (!tfor->data_device) { > + err = -ENOMEM; > + goto fail1; > + } > + > + tfor->irq = usb_alloc_urb(0, GFP_KERNEL); > + if (!tfor->irq) { > + err = -ENOMEM; > + goto fail2; > + } > + > + tfor->out = usb_alloc_urb(0, GFP_KERNEL); > + if (!tfor->irq) { > + err = -ENOMEM; > + goto fail3; > + } > + > + tfor->usbdev = dev; > + tfor->input = input_dev; > + > + usb_make_path(dev, tfor->phys, sizeof(tfor->phys)); > + strlcat(tfor->phys, "/input0", sizeof(tfor->phys)); > + > + input_dev->name = "Tacx Fortius 1942"; > + input_dev->phys = tfor->phys; > + usb_to_input_id(dev, &input_dev->id); > + input_dev->dev.parent = &intf->dev; > + input_set_drvdata(input_dev, tfor); > + input_dev->open = usb_tfor_open; > + input_dev->close = usb_tfor_close; > + input_dev->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY); > + /*handlebar*/ > + input_set_abs_params(input_dev, ABS_WHEEL, -170, 170, 0, 20); > + /*plyn*/ > + input_set_abs_params(input_dev, ABS_GAS, 0, 40, 2, 0); > + /*buttons*/ > + set_bit(BTN_LEFT, input_dev->keybit); > + set_bit(BTN_RIGHT, input_dev->keybit); > + set_bit(BTN_FORWARD, input_dev->keybit); > + set_bit(BTN_BACK, input_dev->keybit); > + > + usb_fill_bulk_urb(tfor->irq, dev, usb_rcvbulkpipe(dev, \ > + BULK_IN_INTERFACE_1942), tfor->data, BIT_IN_DEVICE_1942, \ > + usb_tfor_irq, tfor); > + tfor->irq->transfer_dma = tfor->idata_dma; > + tfor->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; > + > + /* > + * urb sends data to machine > + */ > + > + usb_fill_bulk_urb(tfor->out, dev, usb_sndbulkpipe(dev, \ > + BULK_OUT_INTERFACE_1942), tfor->data_device, BIT_OUT_DEVICE_1942, \ > + usb_tfor_out, tfor); > + tfor->out->transfer_dma = tfor->odata_dma; > + tfor->out->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; > + > + err = input_register_device(tfor->input); > + if (err) > + goto fail2; > + > + usb_set_intfdata(intf, tfor); > + > + return 0; > + > + fail3: usb_buffer_free(dev, BIT_OUT_DEVICE_1942, tfor->data_device, \ > + tfor->odata_dma); > + fail2: usb_buffer_free(dev, BIT_IN_DEVICE_1942, tfor->data, > tfor->idata_dma); + fail1: input_free_device(input_dev); > + kfree(tfor); > + return err; > +} > + > +static struct usb_driver tfor_driver = { > + .name = "fortius_1942", > + .probe = tfor_probe, > + .disconnect = tfor_disconnect, __devexit_p() here please. > + .id_table = tfor_ids, It would be very nice if the driver supported suspend and resume. > +}; > + > +static int __init tfor_init(void) > +{ > + int retval; > + retval = usb_register(&tfor_driver); > + if (retval) > + goto out; > + printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" \ > + DRIVER_DESC "\n"); Our boot is noisy enough, lets drop this message. > +out: > + return retval; > +} > + > +static void __exit tfor_exit(void) > +{ > + usb_deregister(&tfor_driver); > +} > + > +module_init(tfor_init); > +module_exit(tfor_exit); Please CC me when you send updated version. Thanks! -- Dmitry -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html