From cef43c17b0c5c75394fd9fbc4801f8d4f72954e1 Mon Sep 17 00:00:00 2001 From: Ralph Hempel Date: Wed, 19 Nov 2014 20:58:10 -0500 Subject: [PATCH] Wedo Hub support * Add USB HID driver for WeDo hub. * Add wedo bus in sysfs * Add support for Wedo (Power Functions) motors connected to a WeDo hub. * Add support for Wedo Motion and Tilt sensors connected to a WeDo hub. Other changes: * Includes update to the dc motor ops to use direction instead of polarity to set the motor rotation * Rename dc-motor class duty_cycle to duty_cycle_sp. * Add new duty_cycle attribute that returns current duty cycle. --- arch/arm/configs/ev3dev_defconfig | 4 + drivers/legoev3/dc_motor_class.c | 94 +++-- drivers/legoev3/ev3_output_port.c | 22 +- drivers/legoev3/ev3_tacho_motor.c | 4 +- drivers/usb/misc/Kconfig | 2 + drivers/usb/misc/Makefile | 1 + drivers/usb/misc/wedo/Kconfig | 13 + drivers/usb/misc/wedo/Makefile | 7 + drivers/usb/misc/wedo/legowedo.c | 515 +++++++++++++++++++++++++ drivers/usb/misc/wedo/wedo_hub.c | 255 ++++++++++++ drivers/usb/misc/wedo/wedo_motor.c | 125 ++++++ drivers/usb/misc/wedo/wedo_port.c | 356 +++++++++++++++++ drivers/usb/misc/wedo/wedo_sensor.c | 281 ++++++++++++++ include/linux/legoev3/dc_motor_class.h | 22 +- include/linux/wedo/wedo_hub.h | 69 ++++ include/linux/wedo/wedo_motor.h | 28 ++ include/linux/wedo/wedo_port.h | 86 +++++ include/linux/wedo/wedo_sensor.h | 58 +++ 18 files changed, 1897 insertions(+), 45 deletions(-) create mode 100644 drivers/usb/misc/wedo/Kconfig create mode 100644 drivers/usb/misc/wedo/Makefile create mode 100644 drivers/usb/misc/wedo/legowedo.c create mode 100644 drivers/usb/misc/wedo/wedo_hub.c create mode 100644 drivers/usb/misc/wedo/wedo_motor.c create mode 100644 drivers/usb/misc/wedo/wedo_port.c create mode 100644 drivers/usb/misc/wedo/wedo_sensor.c create mode 100644 include/linux/wedo/wedo_hub.h create mode 100644 include/linux/wedo/wedo_motor.h create mode 100644 include/linux/wedo/wedo_port.h create mode 100644 include/linux/wedo/wedo_sensor.h diff --git a/arch/arm/configs/ev3dev_defconfig b/arch/arm/configs/ev3dev_defconfig index 9ecd8a2adb03e..20df023ca580e 100644 --- a/arch/arm/configs/ev3dev_defconfig +++ b/arch/arm/configs/ev3dev_defconfig @@ -159,6 +159,9 @@ CONFIG_SND=m # CONFIG_SND_VERBOSE_PROCFS is not set CONFIG_SND_LEGOEV3=m CONFIG_SND_USB_AUDIO=m +CONFIG_HID=y +CONFIG_HIDRAW=y +CONFIG_HID_GENERIC=y CONFIG_USB=y CONFIG_USB_MON=y CONFIG_USB_OHCI_HCD=y @@ -172,6 +175,7 @@ CONFIG_USB_SERIAL_CP210X=m CONFIG_USB_SERIAL_FTDI_SIO=m CONFIG_USB_SERIAL_PL2303=m CONFIG_USB_LEGOTOWER=m +CONFIG_USB_LEGOWEDO=m CONFIG_NOP_USB_XCEIV=y CONFIG_USB_GADGET=y CONFIG_USB_ETH=m diff --git a/drivers/legoev3/dc_motor_class.c b/drivers/legoev3/dc_motor_class.c index 5836231b72700..5d03e8cc47bf4 100644 --- a/drivers/legoev3/dc_motor_class.c +++ b/drivers/legoev3/dc_motor_class.c @@ -2,6 +2,7 @@ * DC motor device class for LEGO MINDSTORMS EV3 * * Copyright (C) 2014 David Lechner + * Copyright (C) 2014 Ralph Hempel * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -42,9 +43,13 @@ * `commands` (read-only) * : Returns a space separated list of commands supported by the motor controller. * . -* `duty_cycle` (read/write) -* : Sets the duty cycle of the PWM signal sent to the motor. Values are -100 -* to 100 (-100% to 100%). +* `duty_cycle_sp` (read/write) +* : Sets the duty cycle setpoint of the PWM signal sent to the motor. Values +* -100 to 100 (-100% to 100%). +* . +* `duty_cycle` (read) +* : Shows the current duty cycle of the PWM signal sent to the motor. Values +* -100 to 100 (-100% to 100%). * . * `name` (read-only) * : Returns the name of the motor controller's driver. @@ -89,7 +94,7 @@ enum hrtimer_restart dc_motor_class_ramp_timer_handler(struct hrtimer *timer) { struct dc_motor_device *motor = container_of(timer, struct dc_motor_device, ramp_timer); - int ramp_ns, err; + int ramp_ns, err, direction; if (motor->current_duty_cycle == motor->target_duty_cycle) return HRTIMER_NORESTART; @@ -101,10 +106,24 @@ enum hrtimer_restart dc_motor_class_ramp_timer_handler(struct hrtimer *timer) ramp_ns = motor->ramp_down_ms * 10000; motor->current_duty_cycle--; } + + if (0 == ramp_ns) + motor->current_duty_cycle = motor->target_duty_cycle; + hrtimer_forward_now(&motor->ramp_timer, ktime_set(0, ramp_ns)); - err = motor->ops.set_polarity(motor->ops.context, - motor->polarity ^ (motor->current_duty_cycle < 0)); - WARN_ONCE(err, "Failed to set polarity."); + + if (motor->polarity == DC_MOTOR_POLARITY_NORMAL ) + direction = (motor->current_duty_cycle >= 0) + ? DC_MOTOR_DIRECTION_FORWARD + : DC_MOTOR_DIRECTION_REVERSE; + else + direction = (motor->current_duty_cycle <= 0) + ? DC_MOTOR_DIRECTION_FORWARD + : DC_MOTOR_DIRECTION_REVERSE; + + err = motor->ops.set_direction(motor->ops.context, direction ); + WARN_ONCE(err, "Failed to set direction."); + err = motor->ops.set_duty_cycle(motor->ops.context, abs(motor->current_duty_cycle)); WARN_ONCE(err, "Failed to set duty cycle."); @@ -146,7 +165,6 @@ static ssize_t ramp_up_ms_store(struct device *dev, if (sscanf(buf, "%ud", &value) != 1 || value > 10000) return -EINVAL; motor->ramp_up_ms = value; - /* TODO: need to implement ramping */ return count; } @@ -169,7 +187,6 @@ static ssize_t ramp_down_ms_store(struct device *dev, if (sscanf(buf, "%ud", &value) != 1 || value > 10000) return -EINVAL; motor->ramp_down_ms = value; - /* TODO: need to implement ramping */ return count; } @@ -186,14 +203,22 @@ static ssize_t polarity_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct dc_motor_device *motor = to_dc_motor_device(dev); - int i, err; + int i; for (i = 0; i < NUM_DC_MOTOR_POLARITY; i++) { if (sysfs_streq(buf, dc_motor_polarity_values[i])) { - err = motor->ops.set_polarity(motor->ops.context, i); - if (err) - return err; - motor->polarity = i; + if (motor->polarity != i) { + /* + * Force the motor state to get re-evaluated when + * timer expires, makes the motor use ramping even + * if we change direction mid-flight + */ + motor->current_duty_cycle = -motor->current_duty_cycle; + + motor->polarity = i; + hrtimer_start(&motor->ramp_timer, + ktime_set(0, 0), HRTIMER_MODE_REL); + } return count; } @@ -201,21 +226,19 @@ static ssize_t polarity_store(struct device *dev, struct device_attribute *attr, return -EINVAL; } -static ssize_t duty_cycle_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t duty_cycle_sp_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct dc_motor_device *motor = to_dc_motor_device(dev); - int duty_cycle; + int duty_cycle_sp; - duty_cycle = motor->ops.get_duty_cycle(motor->ops.context); - if (motor->polarity == DC_MOTOR_POLARITY_INVERTED) - duty_cycle = -duty_cycle; - return sprintf(buf, "%d\n", duty_cycle); + duty_cycle_sp = motor->target_duty_cycle; + return sprintf(buf, "%d\n", duty_cycle_sp); } -static ssize_t duty_cycle_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t duty_cycle_sp_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { struct dc_motor_device *motor = to_dc_motor_device(dev); int value; @@ -223,12 +246,31 @@ static ssize_t duty_cycle_store(struct device *dev, if (sscanf(buf, "%d", &value) != 1 || value < -100 || value > 100) return -EINVAL; motor->target_duty_cycle = value; + if (motor->ops.get_command(motor->ops.context) == DC_MOTOR_COMMAND_RUN) hrtimer_start(&motor->ramp_timer, ktime_set(0, 0), HRTIMER_MODE_REL); return count; } +static ssize_t duty_cycle_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dc_motor_device *motor = to_dc_motor_device(dev); + int duty_cycle; + unsigned direction; + unsigned polarity; + + duty_cycle = motor->ops.get_duty_cycle(motor->ops.context); + direction = motor->ops.get_direction(motor->ops.context); + polarity = motor->polarity; + + duty_cycle *= ((direction == DC_MOTOR_DIRECTION_FORWARD) ? 1 : -1); + duty_cycle *= ((polarity == DC_MOTOR_POLARITY_NORMAL) ? 1 : -1); + + return sprintf(buf, "%d\n", duty_cycle); +} + static ssize_t commands_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -294,7 +336,8 @@ static DEVICE_ATTR_RO(port_name); static DEVICE_ATTR_RW(ramp_up_ms); static DEVICE_ATTR_RW(ramp_down_ms); static DEVICE_ATTR_RW(polarity); -static DEVICE_ATTR_RW(duty_cycle); +static DEVICE_ATTR_RW(duty_cycle_sp); +static DEVICE_ATTR_RO(duty_cycle); static DEVICE_ATTR_RO(commands); static DEVICE_ATTR_RW(command); @@ -304,6 +347,7 @@ static struct attribute *dc_motor_class_attrs[] = { &dev_attr_ramp_up_ms.attr, &dev_attr_ramp_down_ms.attr, &dev_attr_polarity.attr, + &dev_attr_duty_cycle_sp.attr, &dev_attr_duty_cycle.attr, &dev_attr_commands.attr, &dev_attr_command.attr, diff --git a/drivers/legoev3/ev3_output_port.c b/drivers/legoev3/ev3_output_port.c index fc0f97270c165..7af1f89fc5787 100644 --- a/drivers/legoev3/ev3_output_port.c +++ b/drivers/legoev3/ev3_output_port.c @@ -258,14 +258,14 @@ struct ev3_output_port_data { struct legoev3_port_device *motor; enum ev3_output_port_mode mode; enum dc_motor_command command; - enum dc_motor_polarity polarity; + enum dc_motor_direction direction; }; int ev3_output_port_set_direction_gpios(struct ev3_output_port_data *data) { switch(data->command) { case DC_MOTOR_COMMAND_RUN: - if (data->polarity == DC_MOTOR_POLARITY_NORMAL) { + if (data->direction == DC_MOTOR_DIRECTION_FORWARD) { gpio_direction_output(data->gpio[GPIO_PIN1].gpio, 1); gpio_direction_input(data->gpio[GPIO_PIN2].gpio); } else { @@ -293,7 +293,7 @@ static unsigned ev3_ouput_port_get_supported_commands(void* context) | BIT(DC_MOTOR_COMMAND_BRAKE); } -static int ev3_output_port_get_command(void *context) +static enum dc_motor_command ev3_output_port_get_command(void *context) { struct ev3_output_port_data *data = context; @@ -312,21 +312,21 @@ static int ev3_output_port_set_command(void *context, return ev3_output_port_set_direction_gpios(data); } -static enum dc_motor_polarity ev3_output_port_get_polarity(void *context) +static enum dc_motor_direction ev3_output_port_get_direction(void *context) { struct ev3_output_port_data *data = context; - return data->polarity; + return data->direction; } -static int ev3_output_port_set_polarity(void *context, - enum dc_motor_polarity polarity) +static int ev3_output_port_set_direction(void *context, + enum dc_motor_direction direction) { struct ev3_output_port_data *data = context; - if (data->polarity == polarity) + if (data->direction == direction) return 0; - data->polarity = polarity; + data->direction = direction; return ev3_output_port_set_direction_gpios(data); } @@ -386,8 +386,8 @@ void ev3_output_port_register_motor(struct work_struct *work) ev3_ouput_port_get_supported_commands; pdata.motor_ops.get_command = ev3_output_port_get_command; pdata.motor_ops.set_command = ev3_output_port_set_command; - pdata.motor_ops.get_polarity = ev3_output_port_get_polarity; - pdata.motor_ops.set_polarity = ev3_output_port_set_polarity; + pdata.motor_ops.get_direction = ev3_output_port_get_direction; + pdata.motor_ops.set_direction = ev3_output_port_set_direction; pdata.motor_ops.set_duty_cycle = ev3_output_port_set_duty_cycle; pdata.motor_ops.get_duty_cycle = ev3_output_port_get_duty_cycle; pdata.motor_ops.context = data; diff --git a/drivers/legoev3/ev3_tacho_motor.c b/drivers/legoev3/ev3_tacho_motor.c index 6ea7256097e18..e4b68b622d8bd 100644 --- a/drivers/legoev3/ev3_tacho_motor.c +++ b/drivers/legoev3/ev3_tacho_motor.c @@ -460,14 +460,14 @@ void ev3_tacho_motor_update_output(struct ev3_tacho_motor_data *ev3_tm) int err; if (ev3_tm->power > 0) { - pdata->motor_ops.set_polarity(pdata->motor_ops.context, + pdata->motor_ops.set_direction(pdata->motor_ops.context, ev3_tm->polarity_mode); pdata->motor_ops.set_command(pdata->motor_ops.context, DC_MOTOR_COMMAND_RUN); if (ev3_tm->regulation_mode == TM_REGULATION_OFF && ev3_tm->power < 10) ev3_tm->power = 10; } else if (ev3_tm->power < 0) { - pdata->motor_ops.set_polarity(pdata->motor_ops.context, + pdata->motor_ops.set_direction(pdata->motor_ops.context, !ev3_tm->polarity_mode); pdata->motor_ops.set_command(pdata->motor_ops.context, DC_MOTOR_COMMAND_RUN); diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index 1bca274dc3b52..9fabededfae17 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -158,6 +158,8 @@ config USB_APPLEDISPLAY source "drivers/usb/misc/sisusbvga/Kconfig" +source "drivers/usb/misc/wedo/Kconfig" + config USB_LD tristate "USB LD driver" help diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile index e748fd5dbe94e..d1808f6e2adcb 100644 --- a/drivers/usb/misc/Makefile +++ b/drivers/usb/misc/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_USB_LCD) += usblcd.o obj-$(CONFIG_USB_LD) += ldusb.o obj-$(CONFIG_USB_LED) += usbled.o obj-$(CONFIG_USB_LEGOTOWER) += legousbtower.o +obj-$(CONFIG_USB_LEGOWEDO) += wedo/ obj-$(CONFIG_USB_RIO500) += rio500.o obj-$(CONFIG_USB_TEST) += usbtest.o obj-$(CONFIG_USB_EHSET_TEST_FIXTURE) += ehset.o diff --git a/drivers/usb/misc/wedo/Kconfig b/drivers/usb/misc/wedo/Kconfig new file mode 100644 index 0000000000000..a5be5c9cd36bf --- /dev/null +++ b/drivers/usb/misc/wedo/Kconfig @@ -0,0 +1,13 @@ +# LEGO WeDo device driver + +menuconfig USB_LEGOWEDO + tristate "LEGO WeDo support" + depends on USB + select LEGOEV3_MSENSORS + select LEGOEV3_DC_MOTORS + help + Say Y here if you want to connect a LEGO WeDo hub to your USB + ports. + + To compile this driver as a module, choose M here: the + module will be called legowedo. diff --git a/drivers/usb/misc/wedo/Makefile b/drivers/usb/misc/wedo/Makefile new file mode 100644 index 0000000000000..db31310b8aa89 --- /dev/null +++ b/drivers/usb/misc/wedo/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the LEGO WeDo driver (if driver is inside kernel tree). +# + +obj-$(CONFIG_USB_LEGOWEDO) += legousbwedo.o + +legousbwedo-y := legowedo.o wedo_hub.o wedo_port.o wedo_sensor.o wedo_motor.o diff --git a/drivers/usb/misc/wedo/legowedo.c b/drivers/usb/misc/wedo/legowedo.c new file mode 100644 index 0000000000000..cfd30c71a5169 --- /dev/null +++ b/drivers/usb/misc/wedo/legowedo.c @@ -0,0 +1,515 @@ +/* + * USB Driver for LEGO WeDo + * + * Copyright (C) 2014 Ralph Hempel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * Version Information + */ +#define DRIVER_VERSION "v1.1" +#define DRIVER_AUTHOR "Ralph Hempel " +#define DRIVER_DESC "USB WEDO Driver" +#define DRIVER_LICENSE "GPL" + +#define WEDO_MINOR_BASE 0 + +#define WEDO_STATUS_DEBOUNCE 8 + +/* table of devices that work with this driver */ +static const struct usb_device_id wedo_table [] = { + { USB_DEVICE(0x0694, 0x0003) }, + { }, +}; + +MODULE_DEVICE_TABLE (usb, wedo_table); + +/* Structure to hold all of our device specific stuff */ +struct usb_wedo { + struct usb_device *udev; /* the usb device for this device */ + struct usb_interface *interface; /* the interface for this device */ + + struct wedo_hub_device *whd; /* the wedo hub class for this device */ + + dma_addr_t in_dma; + unsigned char *in_buf; /* The read data buffer */ + struct urb *in_urb; /* the urb to read data with */ + + struct usb_ctrlrequest *cr; /* The USB Control Request */ + + dma_addr_t ctl_dma; + unsigned char *ctl_buf; /* The control data buffer */ + struct urb *ctl_urb; /* the urb to write data with */ + + spinlock_t io_lock; /* lock for io operations */ + + bool status_debounce; /* Status debounce count after output change */ + + bool update_output; /* Output module requested an output change */ + bool output_pending; /* Control URB has been submitted */ + bool io_halt; /* IO to the WeDo hub must stop */ +}; + + +static int wedo_release(struct inode *inode, struct file *file) +{ + return 0; +} + +/* + * The WeDo motor only has the 5V drive voltage of a USB port + * and no ability to regulate the speed. We can modulate the + * speed, but regulation requires velocity or position feedback + * which the PF motor does not have + * + * The port accepts values from 0 to 127 for output level, but + * the motor won't actually start turning until the value is + * around 30. + * + * The duty_cycle value of 1-100 will be scaled from 1-127. + * + * The DIRECTION and duty cycle determine how the motor turns + * + * A duty cycle of 0 when the motor is executing the "run" + * command sets the output equivalent to "coast" or 0x00. + * + * If the motor is set to "brake" then the ouput is set to 0x80. + * + * Note well that the power level to the motor is contained in + * the lower 7 bits of the value that is sent to the hub. The + * high bit is the direction when the motor is running and the + * brake bit when the duty cycle is 0. + */ + +static unsigned char wedo_update_output_value( struct wedo_port_device *wpd ) +{ + int output; + unsigned duty_cycle = wpd->duty_cycle; + + switch (wpd->command) { + + case WEDO_MOTOR_COMMAND_COAST: + output = 0x00; + break; + + case WEDO_MOTOR_COMMAND_BRAKE: + output = 0x80; + break; + + case WEDO_MOTOR_COMMAND_RUN: + if (0 == duty_cycle) { + output = 0x00; + } + else if (WEDO_MOTOR_DIRECTION_FORWARD == wpd->direction) { + output = ((duty_cycle*127)/100); + } else { + output = -((duty_cycle*127)/100); + } + break; + + default: + output = 0x00; + break; + } + return (unsigned char)output; +} + +static void wedo_in_callback (struct urb *urb) +{ + struct usb_wedo *dev = urb->context; + int status = urb->status; + struct wedo_hub_device *whd = dev->whd; + unsigned long flags; + + if (status) { + dev_dbg(&dev->udev->dev, "%s: nonzero status received: %d\n", __func__, status); + goto err_in_urb; + } + + /* + * No need to lock access to the input URB results - the wedo_hub_class + * is the only reader, and each byte of data is atomic. + */ + + if (urb->actual_length == 8) { + whd->from_hub.status.error = (dev->in_buf[0] & 0x80) ? 1 : 0; + whd->from_hub.status.high_power = (dev->in_buf[0] & 0x40) ? 1 : 0; + whd->from_hub.status.echo_bit = (dev->in_buf[0] & 0x01) ? 1 : 0; + + whd->from_hub.voltage = dev->in_buf[1]; + + wedo_hub_update_status( whd ); + + if (whd->wpd[WEDO_PORT_1]) { + whd->wpd[WEDO_PORT_1]->input = dev->in_buf[2]; + whd->wpd[WEDO_PORT_1]->id = dev->in_buf[3]; + + if (0 == whd->wpd[WEDO_PORT_1]->duty_cycle) + wedo_port_update_status ( whd->wpd[WEDO_PORT_1] ); + } + + if (whd->wpd[WEDO_PORT_2]) { + whd->wpd[WEDO_PORT_2]->input = dev->in_buf[4]; + whd->wpd[WEDO_PORT_2]->id = dev->in_buf[5]; + + if (0 == whd->wpd[WEDO_PORT_2]->duty_cycle) + wedo_port_update_status ( whd->wpd[WEDO_PORT_2] ); + } + } + + if (dev->status_debounce < WEDO_STATUS_DEBOUNCE) { + dev->status_debounce++; + } else if (dev->status_debounce == WEDO_STATUS_DEBOUNCE) { + + spin_lock_irqsave (&dev->io_lock, flags); + /* + * Some of the hub output control bits need to be reset or + * forced to the desired state after an output attempt + */ + if (whd->to_hub.status.echo_bit != whd->from_hub.status.echo_bit) { + dev_err(&dev->udev->dev, "Echo mismatch\n"); + } + + whd->to_hub.status.clear_error = 0; + whd->to_hub.status.high_power = whd->from_hub.status.high_power; + whd->to_hub.status.reset = 0; + whd->to_hub.status.echo_bit = whd->from_hub.status.echo_bit ? 0 : 1; + + dev->status_debounce++; + + spin_unlock_irqrestore (&dev->io_lock, flags); + } + + if (dev->update_output && !dev->output_pending) { + + spin_lock_irqsave (&dev->io_lock, flags); + + dev->ctl_buf[0] = 0x00; + + dev->ctl_buf[0] |= (whd->to_hub.status.clear_error ? 0x80 : 0x00); + dev->ctl_buf[0] |= (whd->to_hub.status.high_power ? 0x40 : 0x20); + dev->ctl_buf[0] |= (whd->to_hub.status.shut_down ? 0x10 : 0x00); + dev->ctl_buf[0] |= (whd->to_hub.status.reset ? 0x08 : 0x00); + dev->ctl_buf[0] |= (whd->to_hub.status.echo_bit ? 0x01 : 0x00); + + dev->ctl_buf[1] = wedo_update_output_value (whd->wpd[WEDO_PORT_1]); + dev->ctl_buf[2] = wedo_update_output_value (whd->wpd[WEDO_PORT_2]); + dev->ctl_buf[3] = 0x00; + dev->ctl_buf[4] = 0x00; + dev->ctl_buf[5] = 0x00; + dev->ctl_buf[6] = 0x00; + dev->ctl_buf[7] = 0x00; + + dev->update_output = false; + dev->output_pending = true; + + spin_unlock_irqrestore (&dev->io_lock, flags); + + usb_submit_urb (dev->ctl_urb, GFP_ATOMIC); + } + +err_in_urb: + usb_submit_urb (urb, GFP_ATOMIC); +} + + +static void wedo_ctrl_callback (struct urb *urb) +{ + struct usb_wedo *dev = urb->context; + int status = urb->status; + + if (status) { + dev_dbg(&dev->udev->dev, "%s: nonzero ctl status received: %d\n", __func__, status); + goto err_ctrl_urb; + } + + dev->status_debounce = 0; + dev->output_pending = false; + + return; + +err_ctrl_urb: + usb_submit_urb (urb, GFP_ATOMIC); + return; +} + +void wedo_ctrl_event (struct wedo_hub_device *whd) +{ + struct usb_wedo *wd = whd->wd; + unsigned long flags; + + spin_lock_irqsave (&wd->io_lock, flags); + + if (!wd->io_halt) + wd->update_output = true; + + spin_unlock_irqrestore (&wd->io_lock, flags); + + return; +} + +static const struct file_operations wedo_fops = { + .owner = THIS_MODULE, + .release = wedo_release, +}; + +static struct usb_class_driver wedo_usb_class = { + .name = "wedo%d", + .fops = &wedo_fops, + .minor_base = WEDO_MINOR_BASE, +}; + +static int wedo_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct wedo_hub_device *whd; + struct usb_wedo *dev; + struct usb_endpoint_descriptor *endpoint; + int retval = -ENOMEM; + + /* allocate memory for our device state and initialize it */ + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + dev_err(&interface->dev, "Cannot allocate a usb_wedo device\n"); + goto err_alloc_usb_wedo; + } + + /* allocate memory for our wedo hub device and initialize it */ + whd = kzalloc(sizeof(*whd), GFP_KERNEL); + if (!whd) { + dev_err(&interface->dev, "Cannot allocate a wedo_hub_device\n"); + goto err_alloc_wedo_hub; + } + + dev->udev = usb_get_dev(interface_to_usbdev(interface)); + + dev->interface = interface; + + dev->cr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); + if (!dev->cr) + goto err_alloc_cr; + + dev->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT; + dev->cr->bRequest = HID_REQ_SET_REPORT; + dev->cr->wValue = cpu_to_le16(0x0200); + dev->cr->wIndex = cpu_to_le16(0x00); + dev->cr->wLength = cpu_to_le16(0x08); + + dev->ctl_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->ctl_urb) + goto err_alloc_ctl_urb; + + dev->ctl_buf = usb_alloc_coherent(dev->udev, 8, GFP_KERNEL, &dev->ctl_dma); + if (!dev->ctl_buf) + goto err_alloc_ctl_buf; + + usb_fill_control_urb(dev->ctl_urb, dev->udev, usb_sndctrlpipe(dev->udev, 0), (void *) dev->cr, + dev->ctl_buf, 8, wedo_ctrl_callback, dev); + + dev->ctl_urb->transfer_dma = dev->ctl_dma; + dev->ctl_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + /* We send data to the LEGO WEDO on Endpoint 0 (ctl) and receive data + * from endpoint 1 (in) - there's no sense looping through the endpoints + * but it makes sense to check that the first endpoint is an interrupt + * type, and that it's an input + * + * The control endpoint is available on every USB device, the first + * enumerated endpoint on the interface (array index 0) is endpoint 1 + */ + + endpoint = &interface->cur_altsetting->endpoint[0].desc; + + if (!usb_endpoint_xfer_int(endpoint) && !usb_endpoint_dir_in(endpoint)) + goto err_no_int_in_endpoint; + + dev->in_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->in_urb) + goto err_alloc_in_urb; + + dev->in_buf = usb_alloc_coherent(dev->udev, 8, GFP_KERNEL, &dev->in_dma); + if (!dev->in_buf) + goto err_alloc_in_buf; + + usb_fill_int_urb(dev->in_urb, dev->udev, + usb_rcvintpipe(dev->udev, endpoint->bEndpointAddress), + dev->in_buf, 8, wedo_in_callback, dev, 32 ); + + dev->in_urb->transfer_dma = dev->in_dma; + dev->in_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + /* save our data pointer in this interface device */ + usb_set_intfdata(interface, dev); + + /* we can register the device now, as it is ready */ + retval = usb_register_dev(interface, &wedo_usb_class); + if (retval) { + /* something prevented us from registering this driver */ + dev_err(&interface->dev, + "Not able to get a minor for this device.\n"); + usb_set_intfdata(interface, NULL); + goto err_usb_register_dev; + } + + snprintf (whd->port_name, WEDO_HUB_NAME_SIZE, "usb%s" + ,dev_name (&interface->dev) ); + + retval = register_wedo_hub(whd, &interface->dev); + + if (retval) { + /* something prevented us from registering the wedo hub class */ + dev_err(&interface->dev, + "Not able to register the wedo_hub.\n"); + goto err_register_wedo_hub; + } + + whd->wd = dev; + + dev->whd = whd; + + spin_lock_init( &dev->io_lock ); + + whd->event_callback = wedo_ctrl_event; + + whd->to_hub.status.high_power = 1; + whd->event_callback (whd); + + retval = usb_submit_urb( dev->in_urb, GFP_ATOMIC); + + return 0; + +err_register_wedo_hub: + usb_deregister_dev(interface, &wedo_usb_class); +err_usb_register_dev: + usb_free_coherent (dev->udev, 8, dev->in_buf, dev->in_dma); +err_alloc_in_buf: + usb_free_urb (dev->in_urb); +err_alloc_in_urb: +err_no_int_in_endpoint: + usb_free_coherent (dev->udev, 8, dev->ctl_buf, dev->ctl_dma); +err_alloc_ctl_buf: + usb_free_urb (dev->ctl_urb); +err_alloc_ctl_urb: + kfree (dev->cr); +err_alloc_cr: + usb_put_dev (dev->udev); + kfree (whd); +err_alloc_wedo_hub: + kfree (dev); +err_alloc_usb_wedo: + return retval; +} + +static void wedo_disconnect(struct usb_interface *interface) +{ + struct usb_wedo *dev; + unsigned long flags; + + dev = usb_get_intfdata(interface); + + spin_lock_irqsave (&dev->io_lock, flags); + dev->io_halt = true; + spin_unlock_irqrestore (&dev->io_lock, flags); + + do { + } while (dev->update_output || dev->output_pending); + + unregister_wedo_hub( dev->whd ); + + usb_set_intfdata(interface, NULL); + + /* give back our minor */ + usb_deregister_dev(interface, &wedo_usb_class); + + dev->interface = NULL; + + usb_free_coherent (dev->udev, 8, dev->in_buf, dev->in_dma); + usb_free_urb (dev->in_urb); + usb_free_coherent (dev->udev, 8, dev->ctl_buf, dev->ctl_dma); + usb_free_urb (dev->ctl_urb); + kfree (dev->cr); + usb_put_dev (dev->udev); + kfree (dev->whd); + kfree (dev); +} + +static void wedo_draw_down(struct usb_wedo *dev) +{ +} + +static int wedo_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct usb_wedo *dev = usb_get_intfdata(intf); + + if (!dev) + return 0; + wedo_draw_down(dev); + return 0; +} + +static int wedo_resume(struct usb_interface *intf) +{ + return 0; +} + +static struct usb_driver wedo_driver = { + .name = "legowedo", + .probe = wedo_probe, + .disconnect = wedo_disconnect, + .suspend = wedo_suspend, + .resume = wedo_resume, + .id_table = wedo_table, + .supports_autosuspend = 1, +}; + +static int __init usb_wedo_init(void) +{ + int retval = 0; + + retval = usb_register (&wedo_driver); + if (retval) + goto err_usb_register; + + retval = bus_register (&wedo_bus_type); + if (retval) + goto err_bus_register; + + return 0; + +err_bus_register: + usb_deregister(&wedo_driver); +err_usb_register: + return retval; +} + +static void __exit usb_wedo_exit(void) +{ + bus_unregister (&wedo_bus_type); + usb_deregister (&wedo_driver); +} + +module_init(usb_wedo_init); +module_exit(usb_wedo_exit); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_LICENSE(DRIVER_LICENSE); diff --git a/drivers/usb/misc/wedo/wedo_hub.c b/drivers/usb/misc/wedo/wedo_hub.c new file mode 100644 index 0000000000000..5c721252a960c --- /dev/null +++ b/drivers/usb/misc/wedo/wedo_hub.c @@ -0,0 +1,255 @@ +/* + * Hub functions for LEGO WeDo + * + * Copyright (C) 2014 Ralph Hempel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +/* + * Note: The comment block below is used to generate docs on the ev3dev website. + * Use kramdown (markdown) format. Use a '.' as a placeholder when blank lines + * or leading whitespace is important for the markdown syntax. + */ + +/** + * DOC: website + * + * WeDo Hub + * + * The LEGO WeDo hub device provide a high level interface to the WeDo. + * Each hub has two ports - `port0` and `port1` - that are capable of + * autodetecting WeDo compatible devices that are connected to the WeDo hub. + * . + * The WeDo hub was designed to be a HID device, and the `hidraw` driver + * will automatically bind to the WeDo hub. This behaviour can be + * overridden with the following udev rule: + * . + * . SUBSYSTEM=="usb", ATTRS{idVendor}=="0694", ATTRS{idProduct}=="0003" ACTION=="add", \$ + * . RUN+="/bin/sh -c 'echo $kernel > /sys/bus/usb/drivers/usbhid/unbind; \$ + * . echo $kernel > /sys/bus/usb/drivers/legowedo/bind'" + * . + * ### sysfs Attributes + * . + * WeDo `hub` devices can be found at `/sys/bus/wedo/devices/hub`, where `` + * is incremented each time a WeDo hub is plugged in to a USB port (it is + * not related to which USB port the hub is plugged in to). + * + * Use the `dmesg` log to determine which USB port particular hub device is + * plugged into. + * . + * `error` (read-only) + * : Returns a 1 if the output current source has detected an error - most + * likely due to a stalled motor. If everything is OK, 0 is returned. + * . + * `clear_error` (write-only) + * : Clears an error condition. Write a 1 to begin the clear operation. It + * is not necessary to write a 0 to stop clearing the error. + * . + * `high_power` (read/write) + * : Sets or clears the high power (500 mA) capability of the output port. A + * value of 1 will request 500 mA from the USB port that the WeDo is + * plugged into. Returns 1 of the USB port is able to supply 500 mA to the + * WeDo hub, 0 if only 100 mA is available. + * . + * `shut_down` (write-only) + * : Sets or clears the shut_down state of the WeDo hub. Writing 1 to this + * attribute turns off the output driver for both ports, writing a 0 + * turns the output port back on. + * . + * `reset` (write-only) + * : Sets or clears the reset state of the WeDo hub. Writing 1 to this + * attribute sets the output state to off and clears any errors. + * . + * `voltage` (read-only) + * : Returns the voltage being supplied to the WeDo hub by the USB port, in + * millivolts. The nominal value is 5000. + */ + +#include + +#include +#include + +static ssize_t clear_error_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct wedo_hub_device *whd = to_wedo_hub_device(dev); + int value; + + if (sscanf(buf, "%d", &value) != 1 || value < 0 || value > 1) + return -EINVAL; + whd->to_hub.status.clear_error = value; + whd->event_callback (whd); + + return count; +} + +static ssize_t high_power_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct wedo_hub_device *whd = to_wedo_hub_device(dev); + int value; + + if (sscanf(buf, "%d", &value) != 1 || value < 0 || value > 1) + return -EINVAL; + whd->to_hub.status.high_power = value; + whd->event_callback (whd); + + return count; +} + +static ssize_t high_power_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct wedo_hub_device *whd = to_wedo_hub_device(dev); + + return sprintf(buf, "%d\n", whd->from_hub.status.high_power); +} + +static ssize_t shut_down_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct wedo_hub_device *whd = to_wedo_hub_device(dev); + int value; + + if (sscanf(buf, "%d", &value) != 1 || value < 0 || value > 1) + return -EINVAL; + whd->to_hub.status.shut_down = value; + whd->event_callback (whd); + + return count; +} + +static ssize_t reset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct wedo_hub_device *whd = to_wedo_hub_device(dev); + int value; + + if (sscanf(buf, "%d", &value) != 1 || value < 0 || value > 1) + return -EINVAL; + whd->to_hub.status.reset = value; + whd->event_callback (whd); + + return count; +} + +static ssize_t error_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct wedo_hub_device *whd = to_wedo_hub_device(dev); + + return sprintf(buf, "%d\n", whd->from_hub.status.error); +} + +static ssize_t voltage_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct wedo_hub_device *whd = to_wedo_hub_device(dev); + + return sprintf(buf, "%d\n", whd->from_hub.voltage * 49); +} + +static DEVICE_ATTR_WO(clear_error); +static DEVICE_ATTR_RW(high_power); +static DEVICE_ATTR_WO(shut_down); +static DEVICE_ATTR_WO(reset); +static DEVICE_ATTR_RO(error); +static DEVICE_ATTR_RO(voltage); + +static struct attribute *wedo_hub_device_attrs[] = { + &dev_attr_clear_error.attr, + &dev_attr_high_power.attr, + &dev_attr_shut_down.attr, + &dev_attr_reset.attr, + &dev_attr_error.attr, + &dev_attr_voltage.attr, + NULL +}; + +static struct attribute_group wedo_hub_attribute_group = { + .attrs = wedo_hub_device_attrs +}; + +void wedo_hub_update_status(struct wedo_hub_device *whd) +{ +} + +struct bus_type wedo_bus_type = { + .name = "wedo", +}; + +static void wedo_hub_release(struct device *dev) +{ +} + +static unsigned wedo_hub_id = 0; + +int register_wedo_hub(struct wedo_hub_device *whd, struct device *parent) +{ + int err; + int i = 0; + + if (!whd || !whd->port_name || !parent) + return -EINVAL; + + whd->dev.release = wedo_hub_release; + whd->dev.parent = parent; + whd->dev.bus = &wedo_bus_type; + dev_set_name(&whd->dev, "hub%d", wedo_hub_id++); + + err = device_register(&whd->dev); + if (err) + return err; + + dev_info(&whd->dev, "Bound '%s' to '%s'\n", dev_name(&whd->dev), whd->port_name); + + err = sysfs_create_group(&whd->dev.kobj, &wedo_hub_attribute_group); + if (err) + goto err_sysfs_create_group; + + do { + whd->wpd[i] = register_wedo_port( i, whd ); + if (IS_ERR(whd->wpd[i])) { + err = PTR_ERR(whd->wpd[i]); + goto err_register_wedo_ports; + } + + } while (++i < WEDO_PORT_MAX); + + return 0; + +err_register_wedo_ports: + while (i--) + unregister_wedo_port(whd->wpd[i]); + + sysfs_remove_group(&whd->dev.kobj, &wedo_hub_attribute_group); +err_sysfs_create_group: + device_unregister(&whd->dev); + return err; +} + +void unregister_wedo_hub(struct wedo_hub_device *whd) +{ + int i; + + for (i=0; iwpd[i]); + }; + + sysfs_remove_group(&whd->dev.kobj, &wedo_hub_attribute_group); + + dev_info(&whd->dev, "Unregistered\n"); + device_unregister(&whd->dev); +} diff --git a/drivers/usb/misc/wedo/wedo_motor.c b/drivers/usb/misc/wedo/wedo_motor.c new file mode 100644 index 0000000000000..4d2a7f2b467bb --- /dev/null +++ b/drivers/usb/misc/wedo/wedo_motor.c @@ -0,0 +1,125 @@ +/* + * Motor Definitions for LEGO WeDo + * + * Copyright (C) 2014 Ralph Hempel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +unsigned wedo_get_supported_commands (void* context) +{ + return BIT(DC_MOTOR_COMMAND_RUN) | BIT(DC_MOTOR_COMMAND_COAST) + | BIT(DC_MOTOR_COMMAND_BRAKE); +} + +void wedo_update_output(struct wedo_port_device *wpd) +{ + struct wedo_hub_device *whd = to_wedo_hub_device (wpd->dev.parent); + + whd->event_callback (whd); +} + +static enum wedo_motor_command to_wedo_motor_command[NUM_DC_MOTOR_COMMANDS] = { + [DC_MOTOR_COMMAND_RUN] = WEDO_MOTOR_COMMAND_RUN, + [DC_MOTOR_COMMAND_COAST]= WEDO_MOTOR_COMMAND_COAST, + [DC_MOTOR_COMMAND_BRAKE]= WEDO_MOTOR_COMMAND_BRAKE, +}; + +unsigned wedo_get_command (void* context) +{ + struct wedo_motor_data *wmd = context; + + return wmd->command; +} + +int wedo_set_command (void* context, unsigned command) +{ + struct wedo_motor_data *wmd = context; + + if (wmd->command == command) + return 0; + + wmd->command = command; + + if (NUM_DC_MOTOR_COMMANDS > command) + wmd->wpd->command = to_wedo_motor_command[command]; + + wedo_update_output( wmd->wpd ); + + return 0; +} + +static enum wedo_motor_direction to_wedo_motor_direction[NUM_DC_MOTOR_POLARITY] = { + [DC_MOTOR_DIRECTION_FORWARD] = WEDO_MOTOR_DIRECTION_FORWARD, + [DC_MOTOR_DIRECTION_REVERSE] = WEDO_MOTOR_DIRECTION_REVERSE, +}; + +unsigned wedo_get_direction (void *context) +{ + struct wedo_motor_data *wmd = context; + + return wmd->direction; +} + +int wedo_set_direction (void *context, enum dc_motor_direction direction) +{ + struct wedo_motor_data *wmd = context; + + if (wmd->direction == direction) + return 0; + + wmd->direction = direction; + + if (NUM_DC_MOTOR_DIRECTION > direction) + wmd->wpd->direction = to_wedo_motor_direction[direction]; + + wedo_update_output( wmd->wpd ); + + return 0; +} + +unsigned wedo_get_duty_cycle (void *context) +{ + struct wedo_motor_data *wmd = context; + + return wmd->wpd->duty_cycle; +} + +int wedo_set_duty_cycle (void *context, unsigned duty_cycle) +{ + struct wedo_motor_data *wmd = context; + + if (duty_cycle > 100) + return -EINVAL; + + if (wmd->wpd->duty_cycle == duty_cycle) + return 0; + + wmd->wpd->duty_cycle = duty_cycle; + + wedo_update_output( wmd->wpd ); + + return 0; +} + +const struct dc_motor_ops wedo_motor_ops = { + .get_supported_commands = wedo_get_supported_commands, + .get_command = wedo_get_command, + .set_command = wedo_set_command, + .get_direction = wedo_get_direction, + .set_direction = wedo_set_direction, + .get_duty_cycle = wedo_get_duty_cycle, + .set_duty_cycle = wedo_set_duty_cycle, + .context = NULL, +}; diff --git a/drivers/usb/misc/wedo/wedo_port.c b/drivers/usb/misc/wedo/wedo_port.c new file mode 100644 index 0000000000000..578aefeaafe9e --- /dev/null +++ b/drivers/usb/misc/wedo/wedo_port.c @@ -0,0 +1,356 @@ +/* + * Port functions for LEGO WeDo + * + * Copyright (C) 2014 Ralph Hempel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include + +#include +#include +#include +#include + +/* + * ----------------------------------------------------------------------------- + * This file provides components for interfacing with the ports on the + * LEGO WeDo USB brick. + * + * Each port has its own device node. + * ----------------------------------------------------------------------------- + */ + +struct wedo_id_info { + unsigned char max; + unsigned char *name; +}; + +/* The max fields in this table must be in ascending order for the + * state calculation to work + */ + +const struct wedo_id_info wedo_id_infos[] = { + [WEDO_TYPE_SHORTLO] = { 9 , "shortlo" }, + [WEDO_TYPE_BEND] = { 27 , "bend" }, + [WEDO_TYPE_TILT] = { 47 , "tilt" }, + [WEDO_TYPE_FUTURE] = { 67 , "future" }, + [WEDO_TYPE_RAW] = { 87 , "raw" }, + [WEDO_TYPE_TOUCH] = { 109 , "touch" }, + [WEDO_TYPE_SOUND] = { 131 , "sound" }, + [WEDO_TYPE_TEMP] = { 152 , "temp" }, + [WEDO_TYPE_LIGHT] = { 169 , "light" }, + [WEDO_TYPE_MOTION] = { 190 , "motion" }, + [WEDO_TYPE_LIGHTBRICK] = { 211 , "lightbrick" }, + [WEDO_TYPE_22] = { 224 , "22" }, + [WEDO_TYPE_OPEN] = { 233 , "open" }, + [WEDO_TYPE_MOTOR] = { 246 , "motor" }, + [WEDO_TYPE_SHORTHI] = { 255 , "shorthi" }, +}; + +/* + * These functions handle registering msensor devices on WeDo ports + * as well as the mode callbacks + */ + +static u8 wedo_sensor_get_mode(void *context) +{ + struct wedo_sensor_data *wsd = context; + + return wsd->mode; +} + +static int wedo_sensor_set_mode(void *context, u8 mode) +{ + struct wedo_sensor_data *wsd = context; + + if (mode >= wsd->info.num_modes) + return -EINVAL; + + wsd->mode = mode; + + return 0; +} + +static int register_wedo_sensor (struct wedo_port_device *wpd, enum wedo_sensor_types type) +{ + struct wedo_sensor_data *wsd = dev_get_drvdata(&wpd->dev); + int err; + + if (wsd) + return -EINVAL; + + wsd = kzalloc(sizeof(struct wedo_sensor_data), GFP_KERNEL); + if (!wsd) + return -ENOMEM; + + wsd->wpd = wpd; + + memcpy (&wsd->info, &wedo_sensor_defs[type], sizeof(struct wedo_sensor_info)); + + strncpy (wsd->ms.name, wsd->info.name, MSENSOR_NAME_SIZE); + strncpy (wsd->ms.port_name, wpd->port_name, MSENSOR_NAME_SIZE); + + dev_info(&wpd->dev, "name %s port_name %s\n", wsd->ms.name, wsd->ms.port_name ); + + wsd->ms.num_modes = wsd->info.num_modes; + wsd->ms.mode_info = wsd->info.ms_mode_info; + wsd->ms.get_mode = wedo_sensor_get_mode; + wsd->ms.set_mode = wedo_sensor_set_mode; + + wsd->ms.context = wsd; + + err = register_msensor(&wsd->ms, &wpd->dev); + if (err) + goto err_register_msensor; + + dev_set_drvdata(&wpd->dev, wsd); + + wedo_sensor_set_mode(wsd, 0); + + return 0; + +err_register_msensor: + kfree(wsd); + + return err; +} + +static void unregister_wedo_sensor (struct wedo_port_device *wpd) +{ + struct wedo_sensor_data *wsd = dev_get_drvdata(&wpd->dev); + + if (!wsd) + return; + + unregister_msensor(&wsd->ms); + dev_set_drvdata(&wpd->dev, NULL); + kfree(wsd); +} + +/* + * These functions handle registering dc_motor devices on WeDo ports + */ + +static int register_wedo_motor (struct wedo_port_device *wpd) +{ + struct wedo_motor_data *wmd = dev_get_drvdata(&wpd->dev); + int err; + + if (wmd) + return -EINVAL; + + wmd = kzalloc(sizeof(struct wedo_motor_data), GFP_KERNEL); + if (!wmd) + return -ENOMEM; + + wmd->wpd = wpd; + + strncpy(wmd->md.name, "wedo-motor", DC_MOTOR_NAME_SIZE); + strncpy(wmd->md.port_name, wpd->port_name, DC_MOTOR_NAME_SIZE); + + memcpy(&wmd->md.ops, &wedo_motor_ops, sizeof(struct dc_motor_ops)); + + wmd->md.ops.context = wmd; + + err = register_dc_motor(&wmd->md, &wpd->dev); + if (err) + goto err_register_dc_motor; + + dev_set_drvdata(&wpd->dev, wmd); + + return 0; + +err_register_dc_motor: + kfree(wmd); + + return err; +} + +static void unregister_wedo_motor (struct wedo_port_device *wpd) +{ + struct wedo_motor_data *wmd = dev_get_drvdata(&wpd->dev); + + if (!wmd) + return; + + unregister_dc_motor(&wmd->md); + dev_set_drvdata(&wpd->dev, NULL); + kfree(wmd); +} + +/* + * These functions handle registering devices on WeDo ports. + * + * There are only two generic types if devices that we handle: + * + * Input device ids get registered as msensor class devices + * Output device ids get registered as dc_motor class devices + * + * Currently we only have the tilt and motion sensors for testing + */ + +static int register_wedo_device (struct wedo_port_device *wpd, enum wedo_type_id id) +{ + int err = 0; + + wpd->type_id = id; + + switch( wpd->type_id ) { + + case WEDO_TYPE_TILT: + err = register_wedo_sensor (wpd, WEDO_TILT_SENSOR); + break; + case WEDO_TYPE_MOTION: + err = register_wedo_sensor (wpd, WEDO_MOTION_SENSOR); + break; + case WEDO_TYPE_MOTOR: + err = register_wedo_motor (wpd); + break; + default: + break; + } + + return err; +} + +static void unregister_wedo_device (struct wedo_port_device *wpd) +{ + switch( wpd->type_id ) { + + case WEDO_TYPE_TILT: + case WEDO_TYPE_MOTION: + unregister_wedo_sensor( wpd ); + break; + case WEDO_TYPE_MOTOR: + unregister_wedo_motor (wpd); + break; + default: + break; + } +} + +/* + * Finally, we're at the public driver functions that register the WeDo + * port devices for each hub. + */ + +static void wedo_port_release(struct device *dev) +{ +} + +struct wedo_port_device *register_wedo_port(unsigned port_num, struct wedo_hub_device *whd) +{ + int err; + struct wedo_port_device *wpd; + + if (WEDO_PORT_MAX <= port_num) + return ERR_PTR(-EINVAL); + + /* allocate memory for our new port_device, and initialize it */ + wpd = kzalloc(sizeof(struct wedo_port_device), GFP_KERNEL); + if (!wpd) + return ERR_PTR(-ENOMEM); + + wpd->dev.release = wedo_port_release; + wpd->dev.parent = &whd->dev; + + dev_set_name(&wpd->dev, "port%d", port_num); + + snprintf( wpd->port_name, WEDO_PORT_NAME_SIZE, "%s:wedo%d" + ,whd->port_name, port_num ); + + err = device_register(&wpd->dev); + + if (err) { + dev_err(&wpd->dev, "Failed to register device.\n"); + goto err_wedo_port_register; + } + + return wpd; + +err_wedo_port_register: + kfree(wpd); + + return ERR_PTR(err); +} + +void unregister_wedo_port(struct wedo_port_device *wpd) +{ + if (!wpd) + return; + + unregister_wedo_device( wpd ); + device_unregister(&wpd->dev); + kfree(wpd); +} + +/* Here's where we update the status of the devices connected to the + * LEGO WeDo hub - this function is called when the wedo driver has + * received a complete packet + * + * NOTE: only process ID changes if the output value is 0x00 or 0x80 + */ +#define WEDO_PORT_TYPE_DEBOUNCE 32 + +void wedo_port_update_status(struct wedo_port_device *wpd) +{ + int err = 0; + enum wedo_type_id id; + + struct wedo_sensor_data *wsd = NULL; + struct wedo_motor_data *wmd = NULL; + + switch( wpd->type_id ) { + + case WEDO_TYPE_TILT: + case WEDO_TYPE_MOTION: + wsd = dev_get_drvdata(&wpd->dev); + + if (wsd) { + if (wsd->info.wedo_mode_info[wsd->mode].analog_cb) { + wsd->info.wedo_mode_info[wsd->mode].analog_cb (wsd); + } + } + + break; + case WEDO_TYPE_MOTOR: + wmd = dev_get_drvdata(&wpd->dev); + break; + default: + break; + } + + for (id=0; idid <= wedo_id_infos[id].max ) + break; + + if (id != wpd->temp_type_id) { + wpd->type_debounce = 0; + wpd->temp_type_id = id; + } + else if (WEDO_PORT_TYPE_DEBOUNCE > wpd->type_debounce ) { + wpd->type_debounce++; + } + else if (WEDO_PORT_TYPE_DEBOUNCE == wpd->type_debounce ) { + + if (id != wpd->type_id) { + unregister_wedo_device( wpd ); + + err = register_wedo_device( wpd, id ); + + if (err) + dev_err(&wpd->dev, "Error %d registering device type_id %d to '%s'\n", err, id, dev_name(&wpd->dev)); + } + wpd->type_debounce++; + } +} diff --git a/drivers/usb/misc/wedo/wedo_sensor.c b/drivers/usb/misc/wedo/wedo_sensor.c new file mode 100644 index 0000000000000..55f77538c4449 --- /dev/null +++ b/drivers/usb/misc/wedo/wedo_sensor.c @@ -0,0 +1,281 @@ +/* + * Sensor Definitions for LEGO WeDo + * + * Copyright (C) 2014 Ralph Hempel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "linux/wedo/wedo_sensor.h" +#include "linux/wedo/wedo_hub.h" + +static void wedo_raw_cb(void *context) +{ + struct wedo_sensor_data *wsd = context; + + wsd->ms.mode_info[wsd->mode].raw_data[0] = wsd->wpd->input; +} + +enum wedo_tilt_status_id { + WEDO_TILT_STATUS_UNKNOWN, + WEDO_TILT_STATUS_BACK, + WEDO_TILT_STATUS_RIGHT, + WEDO_TILT_STATUS_LEVEL, + WEDO_TILT_STATUS_FRONT, + WEDO_TILT_STATUS_LEFT, + WEDO_TILT_STATUS_MAX, +}; + +struct wedo_tilt_status_info { + unsigned char max; + unsigned char *name; +}; + +/* The max fields in this table must be in ascending order for the + * state calculation to work + */ + +static const struct wedo_tilt_status_info wedo_tilt_status_infos[] = { + [WEDO_TILT_STATUS_UNKNOWN] = { 0, "unknown" }, + [WEDO_TILT_STATUS_BACK] = { 48, "back" }, + [WEDO_TILT_STATUS_RIGHT] = { 99, "right" }, + [WEDO_TILT_STATUS_LEVEL] = { 153, "level" }, + [WEDO_TILT_STATUS_FRONT] = { 204, "front" }, + [WEDO_TILT_STATUS_LEFT] = { 255, "left" }, +}; + +#define WEDO_TILT_STATUS_DEBOUNCE 4 + +static enum wedo_tilt_status_id wedo_update_tilt_status( struct wedo_sensor_data *wsd ) +{ + enum wedo_tilt_status_id id; + int rawval = wsd->wpd->input; + + for (id=0; iddebounce_status) { + wsd->debounce_count = 0; + wsd->debounce_status = id; + } + else if (WEDO_TILT_STATUS_DEBOUNCE > wsd->debounce_count ) { + wsd->debounce_count++; + } + else if (WEDO_TILT_STATUS_DEBOUNCE == wsd->debounce_count ) { + /* Here's where we'd schedule a notification task */ + wsd->debounce_count++; + wsd->status = id; + } + + return wsd->status; +} + +static void wedo_tilt_axis_cb(void *context) +{ + struct wedo_sensor_data *wsd = context; + + switch (wedo_update_tilt_status (wsd)) + { + case WEDO_TILT_STATUS_BACK: + wsd->ms.mode_info[wsd->mode].raw_data[0] = 0; + wsd->ms.mode_info[wsd->mode].raw_data[1] = -1; + wsd->ms.mode_info[wsd->mode].raw_data[2] = 1; + break; + + case WEDO_TILT_STATUS_RIGHT: + wsd->ms.mode_info[wsd->mode].raw_data[0] = 1; + wsd->ms.mode_info[wsd->mode].raw_data[1] = 0; + wsd->ms.mode_info[wsd->mode].raw_data[2] = 1; + break; + + case WEDO_TILT_STATUS_FRONT: + wsd->ms.mode_info[wsd->mode].raw_data[0] = 0; + wsd->ms.mode_info[wsd->mode].raw_data[1] = 1; + wsd->ms.mode_info[wsd->mode].raw_data[2] = 1; + break; + + case WEDO_TILT_STATUS_LEFT: + wsd->ms.mode_info[wsd->mode].raw_data[0] = -1; + wsd->ms.mode_info[wsd->mode].raw_data[1] = 0; + wsd->ms.mode_info[wsd->mode].raw_data[2] = 1; + break; + + case WEDO_TILT_STATUS_LEVEL: + wsd->ms.mode_info[wsd->mode].raw_data[0] = 0; + wsd->ms.mode_info[wsd->mode].raw_data[1] = 0; + wsd->ms.mode_info[wsd->mode].raw_data[2] = 1; + break; + + case WEDO_TILT_STATUS_UNKNOWN: + default: + wsd->ms.mode_info[wsd->mode].raw_data[0] = 0; + wsd->ms.mode_info[wsd->mode].raw_data[1] = 0; + wsd->ms.mode_info[wsd->mode].raw_data[2] = 0; + break; + } +} + +static void wedo_tilt_status_cb(void *context) +{ + struct wedo_sensor_data *wsd = context; + + switch (wedo_update_tilt_status (wsd)) + { + case WEDO_TILT_STATUS_BACK: + wsd->ms.mode_info[wsd->mode].raw_data[0] = 2; + break; + + case WEDO_TILT_STATUS_RIGHT: + wsd->ms.mode_info[wsd->mode].raw_data[0] = 4; + break; + + case WEDO_TILT_STATUS_FRONT: + wsd->ms.mode_info[wsd->mode].raw_data[0] = 1; + break; + + case WEDO_TILT_STATUS_LEFT: + wsd->ms.mode_info[wsd->mode].raw_data[0] = 3; + break; + + case WEDO_TILT_STATUS_LEVEL: + wsd->ms.mode_info[wsd->mode].raw_data[0] = 0; + break; + + case WEDO_TILT_STATUS_UNKNOWN: + default: + wsd->ms.mode_info[wsd->mode].raw_data[0] = 5; + break; + } +} + +const struct wedo_sensor_info wedo_sensor_defs[] = { + [WEDO_TILT_SENSOR] = { + /** + * @vendor_name: LEGO + * @vendor_part_number: 9584 + * @vendor_part_name: WeDo Tilt Sensor + * @vendor_website: http://education.lego.com/en-us/lego-education-product-database/wedo/9584-tilt-sensor + */ + .name = "wedo-tilt", + .num_modes = 3, + .ms_mode_info = { + [0] = { + /** + * [^tilt-values]: Tilt values: + * + * | Value | Description | + * |-------|-------------| + * | 0 | Level | + * | 1 | Front | + * | 2 | Back | + * | 3 | Left | + * | 4 | Right | + * | 5 | Unknown | + * + * @description: Tilt status + * @value0: Tilt (0 to 5) + * @value0_footnote: [^tilt-values] + */ + .name = "TILT", + .data_sets = 1, + .data_type = MSENSOR_DATA_U8, + }, + [1] = { + /** + * [^axis-values]: Axis values: + * + * | Value0 | Value1 | Value2 | Description | + * |--------|--------|--------|-------------| + * | 0 | 0 | 1 | Level | + * | 0 | 1 | 1 | Front | + * | 0 | -1 | 1 | Back | + * | -1 | 0 | 1 | Left | + * | 1 | 0 | 1 | Right | + * | 0 | 0 | 0 | Unknown | + * + * @description: Tilt around 2 separate axes + * @value0: Tilt Left/Right (-1/0/1) + * @value0_footnote: [^axis-values] + * @value1: Tilt Back/Front (-1/0/1) + * @value1_footnote: [^axis-values] + * @value2: Tilt value valid (0/1) + * @value2_footnote: [^axis-values] + */ + .name = "TILT-AXIS", + .data_sets = 3, + .data_type = MSENSOR_DATA_S8, + }, + [2] = { + /** + * [^raw-values]: Raw values: + * + * | Value | Description | + * |-------|-------------| + * | 0 | Unknown | + * | < 48 | Back | + * | < 99 | Right | + * | < 153 | Level | + * | < 204 | Front | + * | < 255 | Left | + * + * @description: Raw analog value + * @value0: Tilt (0 - 255) + * @value0_footnote: [^raw-values] + */ + .name = "RAW", + .raw_max = 255, + .si_max = 255, + .data_sets = 1, + .data_type = MSENSOR_DATA_U8, + }, + }, + .wedo_mode_info = { + [0] = { + .analog_cb = wedo_tilt_status_cb, + }, + [1] = { + .analog_cb = wedo_tilt_axis_cb, + }, + [2] = { + .analog_cb = wedo_raw_cb, + }, + } + }, + [WEDO_MOTION_SENSOR] = { + /** + * @vendor_name: LEGO + * @vendor_part_number: 9583 + * @vendor_part_name: WeDo Motion Sensor + * @vendor_website: http://education.lego.com/en-us/lego-education-product-database/wedo/9583-motion-sensor + */ + .name = "wedo-motion", + .num_modes = 1, + .ms_mode_info = { + [0] = { + /** + * @description: Raw analog value + * @value0: Motion (0 - 255) + */ + .name = "RAW", + .raw_max = 255, + .si_max = 255, + .data_sets = 1, + .data_type = MSENSOR_DATA_U8, + }, + }, + .wedo_mode_info = { + [0] = { + .analog_cb = wedo_raw_cb, + }, + } + }, +}; + diff --git a/include/linux/legoev3/dc_motor_class.h b/include/linux/legoev3/dc_motor_class.h index 8962b5c8b2dc8..9a15d7d97238e 100644 --- a/include/linux/legoev3/dc_motor_class.h +++ b/include/linux/legoev3/dc_motor_class.h @@ -2,6 +2,7 @@ * DC motor device class for LEGO MINDSTORMS EV3 * * Copyright (C) 2014 David Lechner + * Copyright (C) 2014 Ralph Hempel * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -39,13 +40,20 @@ enum dc_motor_polarity { extern const char* dc_motor_polarity_values[]; +enum dc_motor_direction { + DC_MOTOR_DIRECTION_FORWARD, + DC_MOTOR_DIRECTION_REVERSE, + NUM_DC_MOTOR_DIRECTION +}; + + /** * @get_supported_commands: Return the supported commands as bit flags. * @get_command: Return the current command or negative error. * @set_command: Set the command for the motor. Returns 0 on success or * negative error; - * @get_polarity: Return the current polarity. - * @set_polarity: Set the polarity for the motor. Returns 0 on success or + * @get_direction: Return the current direction. + * @set_direction: Set the direction for the motor. Returns 0 on success or * negative error; * @get_duty_cycle: Returns the current duty cycle in percent (0 to 100). * @set_duty_cycle: Sets the duty cycle. Returns 0 on success or negative error. @@ -53,12 +61,12 @@ extern const char* dc_motor_polarity_values[]; */ struct dc_motor_ops { unsigned (*get_supported_commands)(void* context); - int (*get_command)(void* context); - int (*set_command)(void* context, unsigned command); - unsigned (*get_polarity)(void *context); - int (*set_polarity)(void *context, unsigned polarity); + enum dc_motor_command (*get_command)(void* context); + int (*set_command)(void* context, enum dc_motor_command); + enum dc_motor_direction (*get_direction)(void *context); + int (*set_direction)(void *context, enum dc_motor_direction direction); unsigned (*get_duty_cycle)(void *context); - int (*set_duty_cycle)(void *context, unsigned duty); + int (*set_duty_cycle)(void *context, unsigned duty_cycle); void *context; }; diff --git a/include/linux/wedo/wedo_hub.h b/include/linux/wedo/wedo_hub.h new file mode 100644 index 0000000000000..4e2311cc678c6 --- /dev/null +++ b/include/linux/wedo/wedo_hub.h @@ -0,0 +1,69 @@ +/* + * Hub definitions for LEGO WeDo + * + * Copyright (C) 2014 Ralph Hempel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _LINUX_WEDO_HUB_H +#define _LINUX_WEDO_HUB_H + +#include +#include + +#include +#include +#include + +#define WEDO_HUB_NAME_SIZE 30 + +struct wedo_hub_device { + char name[WEDO_HUB_NAME_SIZE + 1]; + char port_name[WEDO_HUB_NAME_SIZE + 1]; + struct device dev; + + struct usb_wedo *wd; + + struct { + struct { + int clear_error; + int high_power; + int shut_down; + int reset; + int echo_bit; + } status; + } to_hub; + + struct { + struct { + int error; + int high_power; + int echo_bit; + } status; + unsigned char voltage; + } from_hub; + + struct wedo_port_device *wpd[WEDO_PORT_MAX]; + + void (*event_callback)(struct wedo_hub_device *); +}; + +#define to_wedo_hub_device(_dev) container_of(_dev, struct wedo_hub_device, dev) + +extern struct bus_type wedo_bus_type; + +extern int register_wedo_hub(struct wedo_hub_device *, struct device *); +extern void unregister_wedo_hub(struct wedo_hub_device *); + +extern void wedo_hub_update_status(struct wedo_hub_device *); + +#endif /* _LINUX_WEDO_HUB_H */ + diff --git a/include/linux/wedo/wedo_motor.h b/include/linux/wedo/wedo_motor.h new file mode 100644 index 0000000000000..38298258ea692 --- /dev/null +++ b/include/linux/wedo/wedo_motor.h @@ -0,0 +1,28 @@ +/* + * Motor Definitions for LEGO WeDo + * + * Copyright (C) 2014 Ralph Hempel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include + +#include + +extern const struct dc_motor_ops wedo_motor_ops; + +struct wedo_motor_data { + struct wedo_port_device *wpd; + struct dc_motor_device md; + + enum dc_motor_command command; + enum dc_motor_direction direction; +}; diff --git a/include/linux/wedo/wedo_port.h b/include/linux/wedo/wedo_port.h new file mode 100644 index 0000000000000..d09ddb9358d06 --- /dev/null +++ b/include/linux/wedo/wedo_port.h @@ -0,0 +1,86 @@ +/* + * Port definitions for LEGO WeDo + * + * Copyright (C) 2014 Ralph Hempel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _LINUX_WEDO_PORT_H +#define _LINUX_WEDO_PORT_H + +#include + +#define WEDO_PORT_NAME_SIZE 30 + +enum wedo_type_id { + WEDO_TYPE_SHORTLO, + WEDO_TYPE_BEND, + WEDO_TYPE_TILT, + WEDO_TYPE_FUTURE, + WEDO_TYPE_RAW, + WEDO_TYPE_TOUCH, + WEDO_TYPE_SOUND, + WEDO_TYPE_TEMP, + WEDO_TYPE_LIGHT, + WEDO_TYPE_MOTION, + WEDO_TYPE_LIGHTBRICK, + WEDO_TYPE_22, + WEDO_TYPE_OPEN, + WEDO_TYPE_MOTOR, + WEDO_TYPE_SHORTHI, + WEDO_TYPE_MAX, +}; + +enum wedo_ports { + WEDO_PORT_1, + WEDO_PORT_2, + WEDO_PORT_MAX, +}; + +enum wedo_motor_command { + WEDO_MOTOR_COMMAND_RUN, + WEDO_MOTOR_COMMAND_COAST, + WEDO_MOTOR_COMMAND_BRAKE, + NUM_WEDO_MOTOR_COMMANDS +}; + +enum wedo_motor_direction { + WEDO_MOTOR_DIRECTION_FORWARD, + WEDO_MOTOR_DIRECTION_REVERSE, + NUM_WEDO_MOTOR_DIRECTION +}; + +struct wedo_port_device { + struct device dev; + char port_name[WEDO_PORT_NAME_SIZE + 1]; + + int type_debounce; + enum wedo_type_id temp_type_id; + + enum wedo_type_id type_id; + + unsigned char id; + unsigned char input; + + enum wedo_motor_command command; + enum wedo_motor_direction direction; + unsigned duty_cycle; +}; +#define to_wedo_port_device(_dev) container_of(_dev, struct wedo_port_device, dev) + +struct wedo_hub_device; + +extern struct wedo_port_device *register_wedo_port(unsigned port_num, struct wedo_hub_device *); +extern void unregister_wedo_port(struct wedo_port_device *); + +extern void wedo_port_update_status(struct wedo_port_device *); + +#endif /* _LINUX_WEDO_PORT_H */ diff --git a/include/linux/wedo/wedo_sensor.h b/include/linux/wedo/wedo_sensor.h new file mode 100644 index 0000000000000..ee1aec3343f84 --- /dev/null +++ b/include/linux/wedo/wedo_sensor.h @@ -0,0 +1,58 @@ +/* + * Sensor Definitions for LEGO WeDo + * + * Copyright (C) 2014 Ralph Hempel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include + +/** + * struct wedo_sensor_mode_info + * @analog_cb: Analog callback function. Only needed if sensor requires special scaling. + */ +struct wedo_sensor_mode_info { + void (*analog_cb)(void *context); +}; + +enum wedo_sensor_types { + WEDO_TILT_SENSOR, + WEDO_MOTION_SENSOR, +}; + +/** + * struct wedo_sensor_info + * @name: The driver name. Must match name in id_table. + * @ms_mode_info: Array of msensor mode information for each sensor mode. + * @wedo_mode_info: Array of wedo sensor specific mode information for each + * sensor mode. + * @num_modes: Number of valid elements in the mode_info array. + */ +struct wedo_sensor_info { + const char* name; + struct msensor_mode_info ms_mode_info[MSENSOR_MODE_MAX + 1]; + struct wedo_sensor_mode_info wedo_mode_info[MSENSOR_MODE_MAX + 1]; + int num_modes; +}; + +extern const struct wedo_sensor_info wedo_sensor_defs[]; + +struct wedo_sensor_data { + struct wedo_port_device *wpd; + struct msensor_device ms; + struct wedo_sensor_info info; + + int debounce_count; + int debounce_status; + int status; + + u8 mode; +};