Re: [PATCH 2.6.18 real-V5] drivers: add lcd display support

From: Andrew Morton
Date: Tue Sep 26 2006 - 15:08:50 EST


On Fri, 22 Sep 2006 22:03:46 +0200
Miguel Ojeda Sandonis <maxextreme@xxxxxxxxx> wrote:

> miguelojeda-2.6.18-add-lcd-display-support.patch
>
> ...
>
> diff -uprN -X dontdiff linux-2.6.18-vanilla/include/linux/device.h linux-2.6.18/include/linux/device.h
> --- linux-2.6.18-vanilla/include/linux/device.h 2006-09-20 14:52:00.000000000 +0200
> +++ linux-2.6.18/include/linux/device.h 2006-09-20 14:55:56.000000000 +0200
> @@ -271,7 +271,7 @@ struct class_interface {
> extern int class_interface_register(struct class_interface *);
> extern void class_interface_unregister(struct class_interface *);
>
> -extern struct class *class_create(struct module *owner, char *name);
> +extern struct class *class_create(struct module *owner, const char *name);

Please prepare a separate patch for this, send to Greg.

> --- linux-2.6.18-vanilla/drivers/lcddisplay/cfag12864b.c 1970-01-01 01:00:00.000000000 +0100
> +++ linux-2.6.18/drivers/lcddisplay/cfag12864b.c 2006-09-22 21:46:52.000000000 +0200
> @@ -0,0 +1,529 @@
> +/*
> + * Filename: cfag12864b.c
> + * Version: 0.1.0
> + * Description: cfag12864b LCD Display Driver
> + * License: GPLv2
> + * Depends: lcddisplay ks0108
> + *
> + * Author: Miguel Ojeda Sandonis <maxextreme@xxxxxxxxx>
> + * Date: 2006-09-21
> + */
> +
> +
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/fs.h>
> +#include <linux/cdev.h>
> +#include <linux/device.h>
> +#include <linux/lcddisplay.h>
> +#include <linux/ks0108.h>
> +#include <linux/cfag12864b.h>
> +#include <asm/uaccess.h>
> +
> +#define NAME "cfag12864b"
> +#define PRINTK_PREFIX KERN_INFO NAME ": "

In numerous places the driver uses PRINTK_PREFIX for reporting errors. It
should use KERN_ERR. I suggest that PRINTK_PREFIX be removed and that you
open-code all the printk facility levels, make sure that the appropriate
level is being used at each site.

> +
> +
> +/*
> + * Device
> + */
> +
> +static const unsigned int cfag12864b_firstminor;
> +static const unsigned int cfag12864b_ndevices = 1;
> +static const char * cfag12864b_name = NAME;

The more usual kernel style is to not put a space after the `*':

static const char *cfag12864b_name = NAME;

(this affects the entire patch).


Suggest that cfag12864b_name be removed - just use NAME at the single place
where this variable is used.

> +static int cfag12864b_major;
> +static int cfag12864b_minor;
> +static dev_t cfag12864b_device;
> +struct cdev cfag12864b_chardevice;
> +
> +
> +
> +

One blank line is sufficient (there are many instances of this)

> +/*
> + * cfag12864b Commands
> + */
> +
> +static const unsigned int cfag12864b_bits = 8;
> +static const unsigned int cfag12864b_width = CFAG12864B_WIDTH;
> +static const unsigned int cfag12864b_height = CFAG12864B_HEIGHT;
> +static const unsigned int cfag12864b_matrixsize = CFAG12864B_MATRIXSIZE;
> +static const unsigned int cfag12864b_controllers = CFAG12864B_CONTROLLERS;
> +static const unsigned int cfag12864b_pages = CFAG12864B_PAGES;
> +static const unsigned int cfag12864b_addresses = CFAG12864B_ADDRESSES;
> +static const unsigned int cfag12864b_size = CFAG12864B_SIZE;
> +static unsigned char cfag12864b_state;
> +
> +static void cfag12864b_set(void)
> +{
> + ks0108_writecontrol(cfag12864b_state);
> +}
> +
> +static void cfag12864b_setbit(unsigned char state, unsigned char bit)
> +{
> + if (state)
> + set_bit(bit, (void*)&cfag12864b_state);
> + else
> + clear_bit(bit, (void*)&cfag12864b_state);
> + cfag12864b_set();
> +}

bitops are defined on an unsigned long only. This trick is as ugly as sin
and is buggy on big-endian CPUs. Suggest that cfag12864b_state be
converted to unsigned long.

> +{
> + cfag12864b_startline(0);
> +}
> +
> +
> +

blank lines

> +/*
> + * Auxiliary
> + */
> +
> +static void normalizeoffset(unsigned int * offset)

coding style.

> +{
> + if (*offset >= cfag12864b_pages*cfag12864b_addresses)
> + *offset -= cfag12864b_pages*cfag12864b_addresses;
> +}

Ths usual kernel style is to put a single space around arithmetic operators
and around comparison operators. This affects the entire patch, please.


> +void cfag12864b_clear(void)
> +{
> + unsigned char page,address;
> +
> + cfag12864b_setcontrollers(1, 1);
> + for (page=0; page<cfag12864b_pages; ++page) {

For example,

for (page = 0; page < cfag12864b_pages; page++) {

would be more kernelish.

The use of the identifier `page' here is unfortunate. We usually expect
such a thing to have type `struct page *'. I understand that "Each
controller is divided into 8 pages", but it'd be nice if some different
nomenclature could be used here. If nothing else comes to mind, we can
live with it as-is.

> +void cfag12864b_write(
> + unsigned short offset,
> + const unsigned char * buffer,
> + unsigned short count)
> +{

It is more usual to do

void cfag12864b_write(unsigned short offset, const unsigned char *buffer,
unsigned short count)
{

> + for (controller=0; controller < cfag12864b_controllers; ++controller)
> + for (page=0; page < cfag12864b_pages; ++page)
> + for (address=0; address < cfag12864b_addresses; ++address) {
> + dest[(controller*cfag12864b_pages+page)*cfag12864b_addresses+address]=0;
> + for (bit=0; bit < cfag12864b_bits; ++bit)
> + if (src[controller*cfag12864b_addresses+address+(page*cfag12864b_bits+bit)*cfag12864b_width])
> + set_bit(bit, (void*)(dest+(controller*cfag12864b_pages+page)*cfag12864b_addresses+address));
> + }

That's rather ugly-looking.

It's also probably-incorrect on big-endian CPUs. Perhaps you should not
use bitops at all for this driver, use open-coded | and &/~ instead?


The driver doesn't have any locking. Is it racy on SMP and/or
CONFIG_PREEMPT?

> +static int cfag12864b_fopioctlformat(void __user * arg)
> +{
> + int result;
> + int ret = -ENOTTY;
> +
> + unsigned char * tmpbuffer;
> +
> + tmpbuffer = kmalloc(
> + sizeof(unsigned char)*cfag12864b_matrixsize,GFP_KERNEL);
> + if (tmpbuffer == NULL) {
> + printk(PRINTK_PREFIX "FOP ioctl: ERROR: "
> + "can't alloc memory %i bytes\n",
> + sizeof(unsigned char)*cfag12864b_matrixsize);
> + goto none;
> + }
> +
> + result = copy_from_user(
> + tmpbuffer,
> + arg,
> + sizeof(unsigned char)*cfag12864b_matrixsize);

result = copy_from_user(tmpbuffer, arg, cfag12864b_matrixsize);


> + if (result != 0) {
> + printk(PRINTK_PREFIX "FOP ioctl: ERROR: "
> + "can't copy memory from user\n");
> + goto bufferalloced;
> + }
> +
> + cfag12864b_format(tmpbuffer);
> +
> + ret = 0;
> +
> +bufferalloced:
> + kfree(tmpbuffer);
> +
> +none:
> + return ret;
> +}
> +
>
> ...
>
> +
> +static struct file_operations cfag12864b_fops =

`static const struct'

> +
> + class_device_create(
> + lcddisplay_class,NULL,
> + cfag12864b_device,
> + NULL,"cfag12864b%d", cfag12864b_minor);

class_device_create() can fail.

> +void ks0108_writecontrol(unsigned char byte)
> +{
> + const unsigned int ecycledelay = 2;
> + udelay(ecycledelay);
> + parport_write_control(ks0108_parport, byte^(bit(3)|bit(1)|bit(0)));
> +}

udelay(2) would be clearer.

> +void ks0108_displaystate(unsigned char state)
> +{
> + unsigned char cmd = bit(1) | bit(2) | bit(3) | bit(4) | bit(5);
> + if (state)
> + set_bit(0, (void*)&cmd);

argh. Even if this driver will only ever run on big-endian hardware,
please don't do this.

> +void ks0108_startline(unsigned char startline)
> +{
> + const unsigned char maxstartline = 63;
> + unsigned char cmd = bit(6) | bit(7);
> + if (startline > maxstartline)
> + startline = maxstartline;

There are a lot of open-coded min() and max() operations in this driver.
Suggest it be changed to use min() and max().


-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/