All of lore.kernel.org
 help / color / mirror / Atom feed
From: Andrew Morton <akpm@linux-foundation.org>
To: Jaya Kumar <jayakumar.lkml@gmail.com>
Cc: adaplas@pol.net, linux-fbdev-devel@lists.sourceforge.net,
	geert@linux-m68k.org
Subject: Re: [PATCH 1/1 2.6.24] fbdev: defio and Metronomefb v3
Date: Sat, 23 Feb 2008 00:07:00 -0800	[thread overview]
Message-ID: <20080223000700.dc19a7e5.akpm@linux-foundation.org> (raw)
In-Reply-To: <20080218134125.5159.58386.sendpatchset@nxdomain.guide.opendns.com>

On Mon, 18 Feb 2008 08:41:26 -0500 Jaya Kumar <jayakumar.lkml@gmail.com> wrote:

> Hi Tony, Geert, fbdev,
> 
> This patch implements support for the E-Ink Metronome controller. It
> provides an mmapable interface to the controller using defio support.
> 
> I welcome your feedback. Please let me know if it looks okay to apply.
>
> ...
>
> @@ -31,7 +31,7 @@ static int fb_deferred_io_fault(struct vm_area_struct *vma,
>  	unsigned long offset;
>  	struct page *page;
>  	struct fb_info *info = vma->vm_private_data;
> -	/* info->screen_base is in System RAM */
> +	/* info->screen_base is virtual memory */
>  	void *screen_base = (void __force *) info->screen_base;
>  
>  	offset = vmf->pgoff << PAGE_SHIFT;
> @@ -43,6 +43,15 @@ static int fb_deferred_io_fault(struct vm_area_struct *vma,
>  		return VM_FAULT_SIGBUS;
>  
>  	get_page(page);
> +
> +	if (vma->vm_file)
> +		page->mapping = vma->vm_file->f_mapping;
> +	else
> +		printk(KERN_ERR "no mapping available\n");
> +
> +	BUG_ON(!page->mapping);
> +	page->index = vmf->pgoff;
> +
>  	vmf->page = page;
>  	return 0;
>  }

What locking prevents `page' from being stripped off its mapping here?  y
say munmap or truncate (if it's supported here, which it presumably isn't).

> @@ -138,11 +147,20 @@ EXPORT_SYMBOL_GPL(fb_deferred_io_init);
>  
>  void fb_deferred_io_cleanup(struct fb_info *info)
>  {
> +	void *screen_base = (void __force *) info->screen_base;
>  	struct fb_deferred_io *fbdefio = info->fbdefio;
> +	struct page *page;
> +	int i;
>  
>  	BUG_ON(!fbdefio);
>  	cancel_delayed_work(&info->deferred_work);
>  	flush_scheduled_work();
> +
> +	/* clear out the mapping that we setup */
> +	for (i = 0 ; i < info->fix.smem_len; i += PAGE_SIZE) {
> +		page = vmalloc_to_page(screen_base + i);
> +		page->mapping = NULL;
> +	}
>  }

ie: a race with this function?

> +int load_waveform(u8 *mem, u8 *metromem, int m, int t, u8 *frame_count)

This didn't need to be a global symbol.  It would be a poorly chosen
idenfitier if it was global.

> +{
> +	int tta;
> +	int wmta;
> +	int trn = 0;
> +	int i;
> +	unsigned char v;
> +	u8 cksum;
> +	int cksum_idx;
> +	int wfm_idx, owfm_idx;
> +	int mem_idx = 0;
> +	struct waveform_hdr *wfm_hdr;
> +
> +	wfm_hdr = (struct waveform_hdr *) mem;
> +
> +	if (wfm_hdr->fvsn != 1) {
> +		printk(KERN_ERR "Error: bad fvsn %x\n", wfm_hdr->fvsn);
> +		return -EINVAL;
> +	}
> +	if (wfm_hdr->luts != 0) {
> +		printk(KERN_ERR "Error: bad luts %x\n", wfm_hdr->luts);
> +		return -EINVAL;
> +	}
> +	cksum = calc_cksum(32, 47, mem);
> +	if (cksum != wfm_hdr->wfm_cs) {
> +		printk(KERN_ERR "Error: bad cksum %x != %x\n", cksum,
> +					wfm_hdr->wfm_cs);
> +		return -EINVAL;
> +	}
> +	wfm_hdr->mc += 1;
> +	wfm_hdr->trc += 1;
> +	for (i = 0; i < 5; i++) {
> +		if (*(wfm_hdr->stuff2a + i) != 0) {
> +			printk(KERN_ERR "Error: unexpected value in padding\n");
> +			return -EINVAL;
> +		}
> +	}
> +
> +	/* calculating trn. trn is something used to index into
> +	the waveform. presumably selecting the right one for the
> +	desired temperature. it works out the offset of the first
> +	v that exceeds the specified temperature */
> +	for (i = sizeof(*wfm_hdr); i <= sizeof(*wfm_hdr) + wfm_hdr->trc; i++) {
> +		if (mem[i] > t) {
> +			trn = i - sizeof(*wfm_hdr) - 1;
> +			break;
> +		}
> +	}
> +
> +	/* check temperature range table checksum */
> +	cksum_idx = sizeof(*wfm_hdr) + wfm_hdr->trc + 1;
> +	cksum = calc_cksum(sizeof(*wfm_hdr), cksum_idx, mem);
> +	if (cksum != mem[cksum_idx]) {
> +		printk(KERN_ERR "Error: bad temperature range table cksum"
> +				" %x != %x\n", cksum, mem[cksum_idx]);
> +		return -EINVAL;
> +	}
> +
> +	/* check waveform mode table address checksum */
> +	wmta = *((int *) wfm_hdr->wmta) & 0x00FFFFFF;
> +	cksum_idx = wmta + m*4 + 3;
> +	cksum = calc_cksum(cksum_idx - 3, cksum_idx, mem);
> +	if (cksum != mem[cksum_idx]) {
> +		printk(KERN_ERR "Error: bad mode table address cksum"
> +				" %x != %x\n", cksum, mem[cksum_idx]);
> +		return -EINVAL;
> +	}
> +
> +	/* check waveform temperature table address checksum */
> +	tta = *((int *) (mem + wmta + m*4)) & 0x00FFFFFF;

Does this code have any unaligned-access issues on non-x86?

> +	cksum_idx = tta + trn*4 + 3;
> +	cksum = calc_cksum(cksum_idx - 3, cksum_idx, mem);
> +	if (cksum != mem[cksum_idx]) {
> +		printk(KERN_ERR "Error: bad temperature table address cksum"
> +			" %x != %x\n", cksum, mem[cksum_idx]);
> +		return -EINVAL;
> +	}
> +
> +	/* here we do the real work of putting the waveform into the
> +	metromem buffer. this does runlength decoding of the waveform */
> +	owfm_idx = wfm_idx = *((int *) (mem + tta + trn*4)) & 0x00FFFFFF;
> +	while (1) {
> +		unsigned char rl;
> +		v = mem[wfm_idx++];
> +		if (v == wfm_hdr->swtb) {
> +			while ((v = mem[wfm_idx++]) != wfm_hdr->swtb)
> +				metromem[mem_idx++] = v;
> +
> +			continue;
> +		}
> +
> +		if (v == wfm_hdr->endb)
> +			break;
> +
> +		rl = mem[wfm_idx++];
> +		for (i = 0; i <= rl; i++)
> +			metromem[mem_idx++] = v;
> +	}
> +
> +	cksum_idx = wfm_idx;
> +	cksum = calc_cksum(owfm_idx, cksum_idx, mem);
> +	if (cksum != mem[cksum_idx]) {
> +		printk(KERN_ERR "Error: bad waveform data cksum"
> +				" %x != %x\n", cksum, mem[cksum_idx]);
> +		return -EINVAL;
> +	}
> +	*frame_count = (mem_idx/64);
> +
> +	return 0;
> +}
>
> ...
>
> +/* this technique copied from pxafb */
> +static void metronome_disable_lcd_controller(struct metronomefb_par *par)
> +{
> +	DECLARE_WAITQUEUE(wait, current);
> +
> +	set_current_state(TASK_UNINTERRUPTIBLE);
> +	add_wait_queue(&par->waitq, &wait);
> +
> +	LCSR = 0xffffffff;	/* Clear LCD Status Register */
> +	LCCR0 &= ~LCCR0_LDM;	/* Enable LCD Disable Done Interrupt */
> +	LCCR0 |= LCCR0_DIS;	/* Disable LCD Controller */
> +
> +	schedule_timeout(200 * HZ / 1000);
> +	remove_wait_queue(&par->waitq, &wait);
> +}

I assume that if the schedule_timeout() actually times out, we have an
error condition?  Should it be reported?

> +static void metronome_enable_lcd_controller(struct metronomefb_par *par)
> +{
> +	LCSR = 0xffffffff;
> +	FDADR0 = par->metromem_desc_dma;
> +	LCCR0 |= LCCR0_ENB;
> +}
>
> ...
>
> +static int metronome_display_cmd(struct metronomefb_par *par)
> +{
> +	int i;
> +	u16 cs;
> +	u16 opcode;
> +	static u8 borderval;
> +	u8 *ptr;
> +
> +	/* setup display command
> +	we can't immediately set the opcode since the controller
> +	will try parse the command before we've set it all up
> +	so we just set cs here and set the opcode at the end */
> +
> +	ptr = par->metromem;
> +
> +	if (par->metromem_cmd->opcode == 0xCC40)
> +		opcode = cs = 0xCC41;
> +	else
> +		opcode = cs = 0xCC40;
> +
> +	/* set the args ( 2 bytes ) for display */
> +	i = 0;
> +	par->metromem_cmd->args[i] = 	1 << 3 /* border update */
> +					| ((borderval++ % 4) & 0x0F) << 4
> +					| (par->frame_count - 1) << 8;
> +	cs += par->metromem_cmd->args[i++];
> +
> +	/* the rest are 0 */
> +	memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2);
> +
> +	par->metromem_cmd->csum = cs;
> +	par->metromem_cmd->opcode = opcode; /* display cmd */
> +
> +	i = wait_event_interruptible_timeout(par->waitq, (GPLR1 & 0x01), HZ);
> +	return i;
> +}

wait_event_interruptible_timeout() will return immediately if the calling
task has a signal pending.  Will this ause the driver to malfunction?

> +static int __devinit metronome_powerup_cmd(struct metronomefb_par *par)
> +{
> +	int i;
> +	u16 cs;
> +
> +	/* setup power up command */
> +	par->metromem_cmd->opcode = 0x1234; /* pwr up pseudo cmd */
> +	cs = par->metromem_cmd->opcode;
> +
> +	/* set pwr1,2,3 to 1024 */
> +	for (i = 0; i < 3; i++) {
> +		par->metromem_cmd->args[i] = 1024;
> +		cs += par->metromem_cmd->args[i];
> +	}
> +
> +	/* the rest are 0 */
> +	memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2);
> +
> +	par->metromem_cmd->csum = cs;
> +
> +	msleep(1);
> +	metronome_set_gpio_output(RST_GPIO_PIN, 1);
> +
> +	msleep(1);
> +	metronome_set_gpio_output(STDBY_GPIO_PIN, 1);
> +
> +	i = wait_event_interruptible_timeout(par->waitq, (GPLR1 & 0x01), HZ);
> +	return i;
> +}

Ditto

> +static int __devinit metronome_config_cmd(struct metronomefb_par *par)
> +{
> +	int i;
> +	u16 cs;
> +
> +	/* setup config command
> +	we can't immediately set the opcode since the controller
> +	will try parse the command before we've set it all up
> +	so we just set cs here and set the opcode at the end */
> +
> +	cs = 0xCC10;
> +
> +	/* set the 12 args ( 8 bytes ) for config. see spec for meanings */
> +	i = 0;
> +	par->metromem_cmd->args[i] = 	15 /* sdlew */
> +					| 2 << 8 /* sdosz */
> +					| 0 << 11 /* sdor */
> +					| 0 << 12 /* sdces */
> +					| 0 << 15; /* sdcer */
> +	cs += par->metromem_cmd->args[i++];
> +
> +	par->metromem_cmd->args[i] = 	42 /* gdspl */
> +					| 1 << 8 /* gdr1 */
> +					| 1 << 9 /* sdshr */
> +					| 0 << 15; /* gdspp */
> +	cs += par->metromem_cmd->args[i++];
> +
> +	par->metromem_cmd->args[i] = 	18 /* gdspw */
> +					| 0 << 15; /* dispc */
> +	cs += par->metromem_cmd->args[i++];
> +
> +	par->metromem_cmd->args[i] = 	599 /* vdlc */
> +					| 0 << 11 /* dsi */
> +					| 0 << 12; /* dsic */
> +	cs += par->metromem_cmd->args[i++];
> +
> +	/* the rest are 0 */
> +	memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2);
> +
> +	par->metromem_cmd->csum = cs;
> +	par->metromem_cmd->opcode = 0xCC10; /* config cmd */
> +
> +	i = wait_event_interruptible_timeout(par->waitq, (GPLR1 & 0x01), HZ);
> +	return i;
> +}

more...

> +/*
> + * this is the slow path from userspace. they can seek and write to
> + * the fb.
> + */
> +static ssize_t metronomefb_write(struct fb_info *info, const char __user *buf,
> +				size_t count, loff_t *ppos)
> +{
> +	unsigned long p;
> +	int err = -EINVAL;
> +	struct metronomefb_par *par;
> +	unsigned int xres;
> +	unsigned int fbmemlength;
> +
> +	p = *ppos;
> +	par = info->par;
> +	xres = info->var.xres;
> +	fbmemlength = (xres * info->var.yres);
> +
> +	if (p > fbmemlength)
> +		return -ENOSPC;
> +
> +	err = 0;
> +	if ((count + p) > fbmemlength) {
> +		count = fbmemlength - p;
> +		err = -ENOSPC;
> +	}
> +
> +	if (count) {
> +		char *base_addr;
> +
> +		base_addr = (char __force *)info->screen_base;
> +		count -= copy_from_user(base_addr + p, buf, count);
> +		*ppos += count;
> +		err = -EFAULT;
> +	}

This looks odd.  We ignore the return value from copy_from_user(), which
will wreck *ppos.  We also always return -EFAULT.

> +	metronomefb_dpy_update(par);
> +
> +	if (count)
> +		return count;
> +
> +	return err;
> +}
> +

I don't think I'll apply this one yet.

-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/

  parent reply	other threads:[~2008-02-23  8:09 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-02-18 13:41 [PATCH 1/1 2.6.24] fbdev: defio and Metronomefb v3 Jaya Kumar
2008-02-18 14:03 ` Geert Uytterhoeven
2008-02-20 13:18   ` Jaya Kumar
2008-02-20 13:35     ` Andrew Morton
2008-02-20 13:46       ` Jaya Kumar
2008-02-20 23:28         ` Andrew Morton
2008-02-23  8:07   ` Andrew Morton
2008-02-23  8:07 ` Andrew Morton [this message]
2008-02-23 11:05   ` Jaya Kumar
2008-02-23 11:17   ` Jaya Kumar
2008-02-28 18:54     ` Jaya Kumar

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=20080223000700.dc19a7e5.akpm@linux-foundation.org \
    --to=akpm@linux-foundation.org \
    --cc=adaplas@pol.net \
    --cc=geert@linux-m68k.org \
    --cc=jayakumar.lkml@gmail.com \
    --cc=linux-fbdev-devel@lists.sourceforge.net \
    /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.