Re: [staging] driver for adis16255 gyroscope

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Apr 29, 2010 at 8:05 AM, matthias <mensch0815@xxxxxxxxxxxxxx> wrote:
> 2010/4/29 Greg KH <greg@xxxxxxxxx>:
>> On Thu, Apr 29, 2010 at 09:54:08AM +0200, matthias wrote:
>>> 2010/4/28 Greg KH <gregkh@xxxxxxx>:
>>> > On Wed, Apr 28, 2010 at 08:45:35PM +0200, matthias wrote:
>>> >> This drivers allows a communication with the Analog Devices ADIS16255
>>> >> Low Power Gyroscope over SPI.
>>> >
>>> > Why is this going to be a staging driver?  Is there a problem with it
>>> > going into the main portion of the kernel now?
>>>
>>> - No one else has tested it, so I think if it stays in staging, others
>>> have the possibility to test it.
>>> - It has checkpatch issues
>>> - Odd stuff like the AD_CHK(_ss) macro
>>> - I'm not sure to which subsystem it should be added (maybe industrial
>>> io, but this is still staging. Any suggestions?)
>>> - Improvements of code on protocol hierarchy needed (e.g. shutdown
>>> device commands in remove function; it is not obvious which sample
>>> rate the device uses)
>>
>> Ok, can you resend it after fixing the email problem, and add a TODO
>> file that lists these things and puts your email as the person to
>> contact about the driver?
>
> Yes, here we go. I added most of the things Jiri mentioned.

I don't know, this driver is small enough to just fix completely in 1
or 2 more submission reviews, don't see the point in going through
staging.

> Signed-off-by: Matthias Brugger <mensch0815@xxxxxxxxx>
> ---
> diff --git a/drivers/staging/adis16255/Kconfig
> b/drivers/staging/adis16255/Kconfig
> new file mode 100644
> index 0000000..3952cf9
> --- /dev/null
> +++ b/drivers/staging/adis16255/Kconfig
> @@ -0,0 +1,9 @@
> +config ADIS16255
> +       tristate "Ananlog Devices ADIS16250/16255"
> +       depends on SPI && SYSFS
> +       ---help---
> +      If you say yes here you get support for the Analog Devices
> +      ADIS16250/16255 Low Power Gyroscope.

Does this ship on any device BTW? Or will it ? :) It might help to
also put a small description here that all this driver does is expose
the gyroscope's coordinates and orientation via sysfs, and *that's
it*.

> +
> +      This driver can also be built as a module. If so, the module
> +      will be called adis16255.
> diff --git a/drivers/staging/adis16255/Makefile
> b/drivers/staging/adis16255/Makefile
> new file mode 100644
> index 0000000..796dac4
> --- /dev/null
> +++ b/drivers/staging/adis16255/Makefile
> @@ -0,0 +1 @@
> +obj-$(CONFIG_ADIS16255) += adis16255.o
> diff --git a/drivers/staging/adis16255/TODO b/drivers/staging/adis16255/TODO
> new file mode 100644
> index 0000000..31e4ac3
> --- /dev/null
> +++ b/drivers/staging/adis16255/TODO
> @@ -0,0 +1,8 @@
> +* sample rate changeable or at least readable from sysfs
> +* reset gyroscope
> +* encapsulate adis_init and adis_turn_off
> +* AD_CHK deletion
> +* chip selftest in adis_init
> +* reduce kernel messages to reasonable amount
> +
> +Contact: Matthias Brugger <mensch0815@xxxxxxxxx>
> diff --git a/drivers/staging/adis16255/adis16255.c
> b/drivers/staging/adis16255/adis16255.c
> new file mode 100644
> index 0000000..0d8d6bd
> --- /dev/null
> +++ b/drivers/staging/adis16255/adis16255.c
> @@ -0,0 +1,396 @@
> +/*
> + * Analog Devices ADIS16250/ADIS16255 Low Power Gyroscope
> + *
> + * Written by: Matthias Brugger <m_brugger@xxxxxx>

You surely have a copyright too?

> + * Copyright (C) 2010 Fraunhofer Institute for Integrated Circuits
> + *
> + * 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.
> + */
> +
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/device.h>
> +#include <linux/list.h>
> +#include <linux/errno.h>
> +#include <linux/mutex.h>
> +#include <linux/slab.h>
> +
> +#include <linux/interrupt.h>
> +#include <linux/sysfs.h>
> +#include <linux/stat.h>
> +#include <linux/delay.h>
> +
> +#include <linux/gpio.h>
> +
> +#include <linux/spi/spi.h>
> +#include <linux/workqueue.h>
> +
> +#include "adis16255.h"
> +
> +#define ADIS_STATUS        0x3d
> +#define ADIS_SMPL_PRD_MSB  0x37
> +#define ADIS_SMPL_PRD_LSB  0x36
> +#define ADIS_MSC_CTRL_MSB  0x35
> +#define ADIS_MSC_CTRL_LSB  0x34
> +#define ADIS_GPIO_CTRL     0x33
> +#define ADIS_ALM_SMPL1     0x25
> +#define ADIS_ALM_MAG1      0x21
> +#define ADIS_GYRO_SCALE    0x17
> +#define ADIS_GYRO_OUT      0x05
> +#define ADIS_SUPPLY_OUT    0x03
> +#define ADIS_ENDURANCE     0x01
> +
> +/*
> + * data structure for every sensor
> + *
> + * @dev:       Driver model representation of the device.
> + * @spi:       Pointer to the spi device which will manage i/o to spi bus.
> + * @data:      Last read data from device.
> + * @irq_adis:  GPIO Number of IRQ signal
> + * @irq:       irq line manage by kernel
> + * @negative:  indicates if sensor is upside down (negative == 1)
> + * @direction: indicates axis (x, y, z) the sensor is meassuring
> + */
> +struct spi_adis16255_data {
> +       struct device dev;
> +       struct spi_device *spi;
> +       s16 data;
> +       int irq_adis;
> +       int irq;
> +       u8 negative;
> +       char direction;
> +};
> +
> +/*-------------------------------------------------------------------------*/
> +
> +static int spi_adis16255_read_data(struct spi_adis16255_data *spiadis,
> +                                       u8 adr,
> +                                       u8 *rbuf)
> +{
> +       struct spi_device *spi = spiadis->spi;
> +       struct spi_message msg;
> +       struct spi_transfer xfer1, xfer2;
> +       u8 *buf, *rx;
> +       int ret;
> +
> +       buf = kmalloc(4, GFP_KERNEL);

So  this uses kmalloc() but kzalloc() below, why not use on both
kzalloc() and save you those few lines below on the buf?

> +       if (buf == NULL)
> +               return -ENOMEM;
> +
> +       rx = kzalloc(4, GFP_KERNEL);
> +       if (rx == NULL) {
> +               ret = -ENOMEM;
> +               goto err_buf;
> +       }
> +
> +       buf[0] = adr;
> +       buf[1] = 0x00;
> +       buf[2] = 0x00;
> +       buf[3] = 0x00;
> +
> +       spi_message_init(&msg);
> +       memset(&xfer1, 0, sizeof(xfer1));
> +       memset(&xfer2, 0, sizeof(xfer2));
> +
> +       xfer1.tx_buf = buf;
> +       xfer1.rx_buf = buf + 2;
> +       xfer1.len = 2;
> +       xfer1.delay_usecs = 9;
> +
> +       xfer2.tx_buf = rx + 2;
> +       xfer2.rx_buf = rx;
> +       xfer2.len = 2;
> +
> +       spi_message_add_tail(&xfer1, &msg);
> +       spi_message_add_tail(&xfer2, &msg);
> +
> +       ret = spi_sync(spi, &msg);
> +       if (ret == 0) {
> +               rbuf[0] = rx[0];
> +               rbuf[1] = rx[1];
> +       }
> +
> +       kfree(rx);
> +err_buf:
> +       kfree(buf);
> +
> +       return ret;
> +}
> +
> +static int spi_adis16255_write_data(struct spi_adis16255_data *spiadis,
> +                                       u8 adr1,
> +                                       u8 adr2,
> +                                       u8 *wbuf)
> +{
> +       struct spi_device *spi = spiadis->spi;
> +       struct spi_message   msg;
> +       struct spi_transfer  xfer1, xfer2;
> +       u8       *buf, *rx;
> +       int         ret;
> +
> +       buf = kmalloc(4, GFP_KERNEL);
> +       if (buf == NULL)
> +               return -ENOMEM;

In this case not using kzalloc() does save us 4 re-assignments, so that's fine.

> +
> +       rx = kzalloc(4, GFP_KERNEL);
> +       if (rx == NULL) {
> +               ret = -ENOMEM;
> +               goto err_buf;
> +       }
> +
> +       spi_message_init(&msg);
> +       memset(&xfer1, 0, sizeof(xfer1));
> +       memset(&xfer2, 0, sizeof(xfer2));
> +
> +       buf[0] = adr1 | 0x80;
> +       buf[1] = *wbuf;
> +
> +       buf[2] = adr2 | 0x80;
> +       buf[3] = *(wbuf + 1);
> +
> +       xfer1.tx_buf = buf;
> +       xfer1.rx_buf = rx;
> +       xfer1.len = 2;
> +       xfer1.delay_usecs = 9;
> +
> +       xfer2.tx_buf = buf+2;
> +       xfer2.rx_buf = rx+2;
> +       xfer2.len = 2;
> +
> +       spi_message_add_tail(&xfer1, &msg);
> +       spi_message_add_tail(&xfer2, &msg);
> +
> +       ret = spi_sync(spi, &msg);
> +       if (ret != 0)
> +               dev_warn(&spi->dev, "wirte data to %#x %#x failed\n",
> +                               buf[0], buf[2]);
> +
> +       kfree(rx);
> +err_buf:
> +       kfree(buf);
> +       return ret;
> +}
> +
> +/*-------------------------------------------------------------------------*/
> +
> +static irqreturn_t adis_irq_thread(int irq, void *dev_id)
> +{
> +       struct spi_adis16255_data *spiadis = dev_id;
> +       int status;
> +       u16 value;

u16 is unitialized, set it to 0 even though rbuf will be properly set
only if the return status is 0.

> +
> +       status =  spi_adis16255_read_data(spiadis, ADIS_GYRO_OUT, (u8 *)&value);
> +       if (status == 0) {

Better check for the failure first, and bail out immediately with a
goto, that way the below code, the irq handler can be indented all the
way to the left.

> +               /* perform on new data only... */
> +               if (value & 0x8000) {

This interrupt means there is new data?

> +                       /* delete error and new data bit */
> +                       value = value & 0x3fff;
> +                       /* set negative value */
> +                       if (value & 0x2000)
> +                               value = value | 0xe000;
> +
> +                       if (likely(spiadis->negative))
> +                               value = -value;
> +
> +                       spiadis->data = (s16) value;

There's no locking around this guy, this seems wrong.

> +               }
> +       } else {
> +               dev_warn(&spiadis->spi->dev, "SPI FAILED\n");
> +       }
> +
> +       return IRQ_HANDLED;
> +}
> +
> +/*-------------------------------------------------------------------------*/
> +
> +ssize_t adis16255_show_data(struct device *device,
> +               struct device_attribute *da,
> +               char *buf)
> +{
> +       struct spi_adis16255_data *spiadis = dev_get_drvdata(device);
> +       return snprintf(buf, PAGE_SIZE, "%d\n", spiadis->data);
> +}
> +DEVICE_ATTR(data, S_IRUGO , adis16255_show_data, NULL);
> +
> +ssize_t adis16255_show_direction(struct device *device,
> +               struct device_attribute *da,
> +               char *buf)
> +{
> +       struct spi_adis16255_data *spiadis = dev_get_drvdata(device);
> +       return snprintf(buf, PAGE_SIZE, "%c\n", spiadis->direction);
> +}
> +DEVICE_ATTR(direction, S_IRUGO , adis16255_show_direction, NULL);
> +
> +static struct attribute *adis16255_attributes[] = {
> +       &dev_attr_data.attr,
> +       &dev_attr_direction.attr,
> +       NULL
> +};
> +
> +static const struct attribute_group adis16255_attr_group = {
> +       .attrs = adis16255_attributes,
> +};
> +
> +/*-------------------------------------------------------------------------*/
> +
> +static int spi_adis16255_probe(struct spi_device *spi)
> +{
> +
> +#define AD_CHK(_ss)\
> +       do {\
> +               status = _ss;\
> +               if (status != 0)\
> +                       goto irq_err;\
> +       } while (0);
> +
> +       struct adis16255_init_data *init_data = spi->dev.platform_data;
> +       struct spi_adis16255_data  *spiadis;
> +       int status = 0;
> +       u16 value;
> +
> +       spiadis = kzalloc(sizeof(*spiadis), GFP_KERNEL);
> +       if (!spiadis)
> +               return -ENOMEM;
> +
> +       spiadis->spi = spi;
> +       spiadis->irq_adis = init_data->irq;
> +       spiadis->direction = init_data->direction;
> +
> +       if (init_data->negative)
> +               spiadis->negative = 1;
> +
> +       status = gpio_request(spiadis->irq_adis, "adis16255");
> +       if (status != 0)
> +               goto err;
> +
> +       status = gpio_direction_input(spiadis->irq_adis);
> +       if (status != 0)
> +               goto gpio_err;
> +
> +       spiadis->irq = gpio_to_irq(spiadis->irq_adis);
> +
> +       status = request_threaded_irq(spiadis->irq,
> +                       NULL, adis_irq_thread,
> +                       IRQF_DISABLED, "adis-driver", spiadis);
> +
> +       if (status != 0) {
> +               dev_err(&spi->dev, "IRQ request failed\n");
> +               goto gpio_err;
> +       }
> +
> +       dev_dbg(&spi->dev, "GPIO %d IRQ %d\n", spiadis->irq_adis, spiadis->irq);
> +
> +       dev_set_drvdata(&spi->dev, spiadis);
> +       AD_CHK(sysfs_create_group(&spi->dev.kobj, &adis16255_attr_group));
> +
> +       dev_info(&spi->dev, "spi_adis16255 driver added!\n");
> +
> +       AD_CHK(spi_adis16255_read_data(spiadis, ADIS_SUPPLY_OUT, (u8 *)&value));
> +       dev_info(&spi->dev, "sensor works with %d mV (%.4x)!\n",
> +                       ((value & 0x0fff)*18315)/10000,
> +                       (value & 0x0fff));
> +
> +       AD_CHK(spi_adis16255_read_data(spiadis, ADIS_GYRO_SCALE, (u8 *)&value));
> +       dev_info(&spi->dev, "adis GYRO_SCALE is %.4x\n", value);
> +
> +       AD_CHK(spi_adis16255_read_data(spiadis, ADIS_STATUS, (u8 *)&value));
> +       dev_info(&spi->dev, "adis STATUS is %.4x\n", value);
> +
> +       /* timebase = 1.953 ms, Ns = 0 -> 512 Hz sample rate */
> +       value =  0x0001;
> +       AD_CHK(spi_adis16255_write_data(spiadis,
> +                               ADIS_SMPL_PRD_MSB, ADIS_SMPL_PRD_LSB,
> +                               (u8 *)&value));
> +       value = 0x0000;
> +       AD_CHK(spi_adis16255_read_data(spiadis, ADIS_SMPL_PRD_MSB,
> +                               (u8 *)&value));
> +       dev_info(&spi->dev, "adis SMP_PRD is %.4x\n", value);
> +
> +       /* set interrupt on new data... */
> +       value = 0x0006;
> +       AD_CHK(spi_adis16255_write_data(spiadis,
> +                               ADIS_MSC_CTRL_MSB, ADIS_MSC_CTRL_LSB,
> +                               (u8 *)&value));
> +       value = 0x0000;
> +       AD_CHK(spi_adis16255_read_data(spiadis, ADIS_MSC_CTRL_MSB,
> +                               (u8 *)&value));
> +       dev_info(&spi->dev, "adis MSC_CONTROL is %.4x\n", value);
> +
> +       return status;
> +
> +irq_err:
> +       free_irq(spiadis->irq, spiadis);
> +gpio_err:
> +       gpio_free(spiadis->irq_adis);
> +err:
> +       kfree(spiadis);
> +       return status;
> +}
> +
> +static int spi_adis16255_remove(struct spi_device *spi)
> +{
> +       u16 value = 0;
> +       struct spi_adis16255_data  *spiadis    = dev_get_drvdata(&spi->dev);
> +
> +       /* turn sensor off */
> +       spi_adis16255_write_data(spiadis,
> +                       ADIS_SMPL_PRD_MSB, ADIS_SMPL_PRD_LSB,
> +                       (u8 *)&value);
> +       spi_adis16255_write_data(spiadis,
> +                       ADIS_MSC_CTRL_MSB, ADIS_MSC_CTRL_LSB,
> +                       (u8 *)&value);
> +
> +       dev_info(&spi->dev, "unregister: GPIO %d IRQ %d\n",
> +               spiadis->irq_adis, spiadis->irq);
> +
> +       free_irq(spiadis->irq, spiadis);
> +       gpio_free(spiadis->irq_adis);
> +
> +       sysfs_remove_group(&spiadis->spi->dev.kobj, &adis16255_attr_group);
> +
> +       kfree(spiadis);
> +
> +       dev_info(&spi->dev, "spi_adis16255 driver removed!\n");
> +       return 0;
> +}
> +
> +static struct spi_driver spi_adis16255_drv = {
> +       .driver = {
> +               .name =  "spi_adis16255",
> +               .owner = THIS_MODULE,
> +       },
> +       .probe = spi_adis16255_probe,
> +       .remove =   __devexit_p(spi_adis16255_remove),
> +};
> +
> +/*-------------------------------------------------------------------------*/
> +
> +static int __init spi_adis16255_init(void)
> +{
> +       return spi_register_driver(&spi_adis16255_drv);
> +}
> +module_init(spi_adis16255_init);
> +
> +static void __exit spi_adis16255_exit(void)
> +{
> +       spi_unregister_driver(&spi_adis16255_drv);
> +}
> +module_exit(spi_adis16255_exit);
> +
> +MODULE_AUTHOR("Matthias Brugger");
> +MODULE_DESCRIPTION("SPI device driver for ADIS16255 sensor");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/staging/adis16255/adis16255.h
> b/drivers/staging/adis16255/adis16255.h
> new file mode 100644
> index 0000000..03e0700
> --- /dev/null
> +++ b/drivers/staging/adis16255/adis16255.h
> @@ -0,0 +1,12 @@
> +#ifndef ADIS16255_H
> +#define ADIS16255_H
> +
> +#include <linux/types.h>
> +
> +struct adis16255_init_data {
> +       char direction;
> +       u8   negative;
> +       int  irq;
> +};
> +
> +#endif
>
> --
> motzblog.wordpress.com
> _______________________________________________
> devel mailing list
> devel@xxxxxxxxxxxxxxxxxxxxxx
> http://driverdev.linuxdriverproject.org/mailman/listinfo/devel
>
_______________________________________________
devel mailing list
devel@xxxxxxxxxxxxxxxxxxxxxx
http://driverdev.linuxdriverproject.org/mailman/listinfo/devel


[Index of Archives]     [Linux Driver Backports]     [DMA Engine]     [Linux GPIO]     [Linux SPI]     [Video for Linux]     [Linux USB Devel]     [Linux Coverity]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux