All of lore.kernel.org
 help / color / mirror / Atom feed
From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
To: "Felipe F. Tonello" <eu@felipetonello.com>
Cc: Jiri Slaby <jslaby@suse.cz>,
	linux-kernel@vger.kernel.org, linux-serial@vger.kernel.org
Subject: Re: [PATCH] char: Added support for u-blox 6 i2c gps driver
Date: Tue, 13 Jan 2015 17:33:50 -0800	[thread overview]
Message-ID: <20150114013350.GA11620@kroah.com> (raw)
In-Reply-To: <1421198202-4042-1-git-send-email-eu@felipetonello.com>

On Tue, Jan 13, 2015 at 05:16:42PM -0800, Felipe F. Tonello wrote:
> This driver will basically translate serial communication to i2c communication
> between the user-space and the GPS module.
> 
> It creates a /dev/ttyS device node.
> 
> There are specific tty termios flags in order to the tty line discipliner not
> to change the date it is pushed to user space.

I don't understand this sentance, what date?  What is pushed where?
What termios files?  What is a "discipliner"?

> That is necessary so the NMEA
> data is not corrupted.
> * iflags: added IGNCR: Ignore carriage return on input.
> * oflags: removed ONLCR: (XSI) Map NL to CR-NL on output.
> 
> Signed-off-by: Felipe F. Tonello <eu@felipetonello.com>
> ---
>  drivers/tty/serial/Kconfig          |   9 ++
>  drivers/tty/serial/Makefile         |   1 +
>  drivers/tty/serial/ublox6-gps-i2c.c | 245 ++++++++++++++++++++++++++++++++++++
>  3 files changed, 255 insertions(+)
>  create mode 100644 drivers/tty/serial/ublox6-gps-i2c.c
> 
> diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
> index 26cec64..49913eb 100644
> --- a/drivers/tty/serial/Kconfig
> +++ b/drivers/tty/serial/Kconfig
> @@ -1552,6 +1552,15 @@ config SERIAL_MEN_Z135
>  	  This driver can also be build as a module. If so, the module will be called
>  	  men_z135_uart.ko
>  
> +config SERIAL_UBLOX_GPS
> +	tristate "u-blox 6 I2C GPS support"
> +	depends on I2C
> +	default n
> +	help
> +	  This driver uses i2c to communicate with the u-blox 6 GPS module
> +	  and has a serial tty device interface to the user-space, making
> +	  it easy to read/write data from/to the GPS.
> +
>  endmenu
>  
>  config SERIAL_MCTRL_GPIO
> diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
> index 0080cc3..cba2b5c 100644
> --- a/drivers/tty/serial/Makefile
> +++ b/drivers/tty/serial/Makefile
> @@ -92,6 +92,7 @@ obj-$(CONFIG_SERIAL_ARC)	+= arc_uart.o
>  obj-$(CONFIG_SERIAL_RP2)	+= rp2.o
>  obj-$(CONFIG_SERIAL_FSL_LPUART)	+= fsl_lpuart.o
>  obj-$(CONFIG_SERIAL_MEN_Z135)	+= men_z135_uart.o
> +obj-$(CONFIG_SERIAL_UBLOX_GPS) += ublox6-gps-i2c.o
>  
>  # GPIOLIB helpers for modem control lines
>  obj-$(CONFIG_SERIAL_MCTRL_GPIO)	+= serial_mctrl_gpio.o
> diff --git a/drivers/tty/serial/ublox6-gps-i2c.c b/drivers/tty/serial/ublox6-gps-i2c.c
> new file mode 100644
> index 0000000..16dd1cf
> --- /dev/null
> +++ b/drivers/tty/serial/ublox6-gps-i2c.c
> @@ -0,0 +1,245 @@
> +/* u-blox 6 I2C GPS driver
> + *
> + * Copyright (C) 2015 Felipe F. Tonello <eu@felipetonello.com>
> + *
> + * Driver that translates a serial tty GPS device to a i2c GPS device

I don't think there is a "serial tty GPS device" here, right?


> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/tty.h>
> +#include <linux/tty_flip.h>
> +#include <linux/i2c.h>
> +#include <linux/workqueue.h>
> +
> +/*
> + * Version Information
> + */
> +#define DRIVER_VERSION "v0.1"
> +#define DRIVER_DESC "u-blox 6 I2C GPS driver"
> +
> +#define UBLOX_GPS_MAJOR 0
> +#define UBLOX_GPS_NUM 1 /* Only support 1 GPS at a time */
> +
> +/* By default u-blox GPS fill its buffer every 1 second (1000 msecs) */
> +#define READ_TIME 1000
> +
> +static struct tty_port *ublox_gps_tty_port;
> +static struct i2c_client *ublox_gps_i2c_client;

Why only one device/client?  Why can't you have more than one in the
system?  Please don't make this work for only one device, these can all
be device-private.

> +static int ublox_gps_is_open;

Why do you need this?  And again, don't make it global for the whole
driver, but unique per-device

> +static struct file *ublox_gps_filp;

I don't understand why you even need this, what problem is this solving?

> +static void ublox_gps_read_worker(struct work_struct *private);
> +
> +static DECLARE_DELAYED_WORK(ublox_gps_wq, ublox_gps_read_worker);

Again, make device-specific.

> +static void ublox_gps_read_worker(struct work_struct *private)
> +{
> +	s32 gps_buf_size, buf_size = 0;
> +	u8 *buf;
> +
> +	if (!ublox_gps_is_open)
> +		return;

How can this happen?

> +
> +	/* check if driver was removed */
> +	if (!ublox_gps_i2c_client)
> +		return;

How can this happen?

> +
> +	gps_buf_size = i2c_smbus_read_word_data(ublox_gps_i2c_client, 0xfd);
> +	if (gps_buf_size < 0) {
> +		dev_warn(&ublox_gps_i2c_client->dev, KBUILD_MODNAME ": couldn't read register(0xfd) from GPS.\n");

Something to be fixed for all of your dev_*() calls, don't put
KBUILD_MODNAME, it's not needed as dev_* handles everything properly to
point to the specific driver and device that created the message.


> +		/* try one more time */
> +		goto end;
> +	}
> +
> +	/* 0xfd is the MSB and 0xfe is the LSB */
> +	gps_buf_size = ((gps_buf_size & 0xf) << 8) | ((gps_buf_size & 0xf0) >> 8);

I don't understand the comment here.

> +
> +	if (gps_buf_size > 0) {
> +
> +		buf = kcalloc(gps_buf_size, sizeof(*buf), GFP_KERNEL);
> +		if (!buf) {
> +			dev_warn(&ublox_gps_i2c_client->dev, KBUILD_MODNAME ": couldn't allocate memory.\n");

No need to send any error if memory is gone, that already happened in
the syslog.

> +			/* try one more time */
> +			goto end;
> +		}
> +
> +		do {
> +			buf_size = i2c_master_recv(ublox_gps_i2c_client, (char *)buf, gps_buf_size);
> +			if (buf_size < 0) {
> +				dev_warn(&ublox_gps_i2c_client->dev, KBUILD_MODNAME ": couldn't read data from GPS.\n");
> +				kfree(buf);
> +				/* try one more time */
> +				goto end;
> +			}
> +
> +			tty_insert_flip_string(ublox_gps_tty_port, buf, buf_size);
> +
> +			gps_buf_size -= buf_size;
> +
> +			/* There is a small chance that we need to split the data over
> +			   several buffers. If this is the case we must loop */
> +		} while (unlikely(gps_buf_size > 0));
> +
> +		tty_flip_buffer_push(ublox_gps_tty_port);
> +
> +		kfree(buf);
> +	}
> +
> +end:
> +	/* resubmit the workqueue again */
> +	schedule_delayed_work(&ublox_gps_wq, msecs_to_jiffies(READ_TIME)); /* 1 sec delay */
> +}
> +
> +static int ublox_gps_serial_open(struct tty_struct *tty, struct file *filp)
> +{
> +	if (ublox_gps_is_open)
> +		return -EBUSY;
> +
> +	ublox_gps_filp = filp;
> +	ublox_gps_tty_port = tty->port;
> +	ublox_gps_tty_port->low_latency = true; /* make sure we push data immediately */
> +	ublox_gps_is_open = true;
> +
> +	schedule_delayed_work(&ublox_gps_wq, 0);
> +
> +	return 0;
> +}
> +
> +static void ublox_gps_serial_close(struct tty_struct *tty, struct file *filp)
> +{
> +	if (!ublox_gps_is_open)

How can this happen?

> +		return;
> +
> +	/* avoid stop when the denied (in open) file structure closes itself */
> +	if (ublox_gps_filp != filp)
> +		return;

I don't understand, how can something "close itself"?

> +
> +	ublox_gps_is_open = false;
> +	ublox_gps_filp = NULL;
> +	ublox_gps_tty_port = NULL;
> +}
> +
> +static int ublox_gps_serial_write(struct tty_struct *tty, const unsigned char *buf,
> +	int count)
> +{
> +	if (!ublox_gps_is_open)
> +		return 0;
> +
> +	/* check if driver was removed */
> +	if (!ublox_gps_i2c_client)
> +		return 0;
> +
> +	/* we don't write back to the GPS so just return same value here */
> +	return count;
> +}

So write does nothing, why even have it at all?

> +static int ublox_gps_write_room(struct tty_struct *tty)
> +{
> +	if (!ublox_gps_is_open)
> +		return 0;
> +
> +	/* check if driver was removed */
> +	if (!ublox_gps_i2c_client)
> +		return 0;
> +
> +	/* we don't write back to the GPS so just return some value here */
> +	return 1024;

Why have this function at all if it does nothing?


> +}
> +
> +static const struct tty_operations ublox_gps_serial_ops = {
> +	.open = ublox_gps_serial_open,
> +	.close = ublox_gps_serial_close,
> +	.write = ublox_gps_serial_write,
> +	.write_room = ublox_gps_write_room,
> +};
> +
> +static struct tty_driver *ublox_gps_tty_driver;
> +
> +static int ublox_gps_probe(struct i2c_client *client,
> +	const struct i2c_device_id *id)
> +{
> +	int result = 0;
> +
> +	ublox_gps_tty_driver = alloc_tty_driver(UBLOX_GPS_NUM);
> +	if (!ublox_gps_tty_driver)
> +		return -ENOMEM;
> +
> +	ublox_gps_tty_driver->owner = THIS_MODULE;
> +	ublox_gps_tty_driver->driver_name = "ublox_gps";
> +	ublox_gps_tty_driver->name = "ttyS";
> +	ublox_gps_tty_driver->major = UBLOX_GPS_MAJOR;
> +	ublox_gps_tty_driver->minor_start = 0;
> +	ublox_gps_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
> +	ublox_gps_tty_driver->subtype = SERIAL_TYPE_NORMAL;
> +	ublox_gps_tty_driver->flags = TTY_DRIVER_REAL_RAW;
> +	ublox_gps_tty_driver->init_termios = tty_std_termios;
> +	ublox_gps_tty_driver->init_termios.c_iflag = IGNCR | IXON;
> +	ublox_gps_tty_driver->init_termios.c_oflag = OPOST;
> +	ublox_gps_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD |
> +		HUPCL | CLOCAL;
> +	ublox_gps_tty_driver->init_termios.c_ispeed = 9600;
> +	ublox_gps_tty_driver->init_termios.c_ospeed = 9600;
> +	tty_set_operations(ublox_gps_tty_driver, &ublox_gps_serial_ops);
> +	result = tty_register_driver(ublox_gps_tty_driver);
> +	if (result) {
> +		dev_err(&ublox_gps_i2c_client->dev, KBUILD_MODNAME ": %s - tty_register_driver failed\n",
> +			__func__);
> +		goto err;
> +	}
> +
> +	ublox_gps_i2c_client = client;
> +	ublox_gps_filp = NULL;
> +	ublox_gps_tty_port = NULL;
> +	ublox_gps_is_open = false;
> +
> +	/* i2c_set_clientdata(client, NULL); */
> +
> +	dev_info(&ublox_gps_i2c_client->dev, KBUILD_MODNAME ": " DRIVER_VERSION ": "
> +		DRIVER_DESC "\n");

Be quiet for "normal" operations please, your driver should never send
anything to the kernel log unless some error happens.

thanks,

greg k-h

  reply	other threads:[~2015-01-14  1:33 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-01-14  1:16 [PATCH] char: Added support for u-blox 6 i2c gps driver Felipe F. Tonello
2015-01-14  1:33 ` Greg Kroah-Hartman [this message]
2015-01-14  2:07   ` Felipe Tonello
2015-01-14  4:10     ` Greg Kroah-Hartman
2015-01-14 15:48 ` One Thousand Gnomes
2015-01-14 18:39   ` Felipe Tonello
2015-01-14 20:43     ` Greg Kroah-Hartman
2015-01-15 11:29     ` One Thousand Gnomes

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20150114013350.GA11620@kroah.com \
    --to=gregkh@linuxfoundation.org \
    --cc=eu@felipetonello.com \
    --cc=jslaby@suse.cz \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-serial@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.