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/
next prev 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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).