Discussion:
[uClinux-dev] m68k: trying accessing fb memory
angelo
2012-12-12 15:48:00 UTC
Permalink
Dear All,

i am running
Linux 2.6.36.2 main line, over uClinux on a ColdFire mcf5307.

I have written then a little framebuffer driver for a 128x64 graphic lcd.
Driver works fine until file write operations are used, and also,
console output works fine when linked with "fbcon".


I cannot in anyway get access to it from userspace, as a memory area,
i implemented the "fb_mmap" routine, then tried:

fd = open("/dev/fb0", O_RDWR);

buffer = mmap( 0, buflen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 );
This fails (-1)
Then i tried:
buffer = mmap( 0, buflen, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0 );
works, returns a valid pointer, but i cannot see any change on the
screen writing in this area.

Any help is very appreciated.
Angelo Dureghello
--
Gavin Lambert
2012-12-12 21:37:21 UTC
Permalink
Post by angelo
I have written then a little framebuffer driver for a 128x64 graphic
lcd.
[...]
Post by angelo
buffer = mmap( 0, buflen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 );
buffer = mmap( 0, buflen, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0
); works, returns a valid pointer, but i cannot see any change on the
screen writing in this area.
Have you read the nommu mmap documentation in the kernel tree? You have to
implement an extra handler in your driver in order to support shared mmaps
on nommu.
angelo
2012-12-12 21:46:21 UTC
Permalink
Hi Lambert,

thanks, well, i implemented the handler:

static int amcorefb_mmap(struct fb_info *info, struct vm_area_struct *vma)
{
printk(KERN_INFO "AMCORE frame buffer: mmapping\r\n");

...

}

static struct fb_ops amcorefb_ops = {
.owner = THIS_MODULE,
.fb_open = amcorefb_open,
.fb_write = amcorefb_write,
.fb_release = amcorefb_release,
.fb_pan_display = amcorefb_pan_display,
.fb_fillrect = amcorefb_fillrect,
.fb_copyarea = amcorefb_copyarea,
.fb_imageblit = amcorefb_imageblit,
--> .fb_mmap = amcorefb_mmap,
};

But "amcorefb_mmap" never get called (no printk output).

mmap(), when used with MAP_SHARED, returns error 19 (ENODEV)

Looking inside nommu.c, seems the error is generated from this line:

if (flags & MAP_SHARED) {
/* do checks for writing, appending and locking */
if ((prot & PROT_WRITE) &&
!(file->f_mode & FMODE_WRITE))
return -EACCES;

if (IS_APPEND(file->f_path.dentry->d_inode) &&
(file->f_mode & FMODE_WRITE))
return -EACCES;

if (locks_verify_locked(file->f_path.dentry->d_inode))
return -EAGAIN;

--> if (!(capabilities & BDI_CAP_MAP_DIRECT))
--> return -ENODEV;


Seems i miss the "get_unmapped_area/BDI_CAP_MAP_DIRECT) capability.
Could this be due to the fact i am using a mainline kernel ?

Regards,
Angelo
Post by Gavin Lambert
Post by angelo
I have written then a little framebuffer driver for a 128x64 graphic
lcd.
[...]
Post by angelo
buffer = mmap( 0, buflen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 );
buffer = mmap( 0, buflen, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0
); works, returns a valid pointer, but i cannot see any change on the
screen writing in this area.
Have you read the nommu mmap documentation in the kernel tree? You have to
implement an extra handler in your driver in order to support shared mmaps
on nommu.
--
Gavin Lambert
2012-12-12 22:21:55 UTC
Permalink
Post by angelo
static int amcorefb_mmap(struct fb_info *info, struct vm_area_struct
*vma) {
No, that's not the right one. You don't need to implement that.
Post by angelo
Seems i miss the "get_unmapped_area/BDI_CAP_MAP_DIRECT) capability.
fbmem.c implements the main get_unmapped_area handler. In your framebuffer
driver you just need to either implement fb_get_unmapped_area or (easier,
but only works on nommu) set fix.smem_start and .smem_len correctly in your
fb_info.
angelo
2012-12-13 09:00:52 UTC
Permalink
I grepped all the linux tree for "HAVE_ARCH_FB_UNMAPPED_AREA".

arch/sparc/include/asm/pgtable_64.h:#define HAVE_ARCH_FB_UNMAPPED_AREA
arch/blackfin/include/asm/pgtable.h:#define HAVE_ARCH_FB_UNMAPPED_AREA
drivers/video/fbmem.c:#ifdef HAVE_ARCH_FB_UNMAPPED_AREA

Seems that for my case (m68k-nommu) it is not defined, so none of the
get_unmapped_area will be called, in any place.


Bu in fbmem.c there is a "fb_mmap" that's probably called.

I will try to set correctly as you said fix.smem_start and .smem_len
and let you know.

Thanks
Angelo
Post by Gavin Lambert
Post by angelo
static int amcorefb_mmap(struct fb_info *info, struct vm_area_struct
*vma) {
No, that's not the right one. You don't need to implement that.
Post by angelo
Seems i miss the "get_unmapped_area/BDI_CAP_MAP_DIRECT) capability.
fbmem.c implements the main get_unmapped_area handler. In your framebuffer
driver you just need to either implement fb_get_unmapped_area or (easier,
but only works on nommu) set fix.smem_start and .smem_len correctly in your
fb_info.
_______________________________________________
uClinux-dev mailing list
uClinux-dev at uclinux.org
http://mailman.uclinux.org/mailman/listinfo/uclinux-dev
This message was resent by uclinux-dev at uclinux.org
http://mailman.uclinux.org/mailman/options/uclinux-dev
--
angelo
2012-12-13 14:20:46 UTC
Permalink
I confirm there is no chance to use framebuffer mmap() and MAP_SHARED to
access framebuffer screen memory with _coldfire_ _mcf5307_.

Actually, i call

buffer = mmap(0, buflen, PROT_WRITE, MAP_SHARED, fd, 0);

over fd open on "/dev/fb0" as ORDWR.

Call fail inside
mm/nommu.c, validate_mmap_request(), line 920



if (flags & MAP_SHARED) {
/* do checks for writing, appending and locking */
if ((prot & PROT_WRITE) &&
!(file->f_mode & FMODE_WRITE))
return -EACCES;

if (IS_APPEND(file->f_path.dentry->d_inode) &&
(file->f_mode & FMODE_WRITE))
return -EACCES;

if
(locks_verify_locked(file->f_path.dentry->d_inode))
return -EAGAIN;

if (!(capabilities & BDI_CAP_MAP_DIRECT))
{
--> printk (KERN_INFO "ENODEV 2\n");
--> return -ENODEV;
}


So, as i explained, i cannot have BDI_CAP_MAP_DIRECT for this
architecture, and the mmap call using MAP_SHARED fails.

Looking the code, error is still at a mmap params "validation" level,
and far from any "fb_mmap" to get called.

Code is complex, and still i can miss something. Anyway i hope you can
confirm i cannot use mmap() to access framebuffer for this cpu, or
maybe, you can suggest me some example that can do it.

Regards,
Angelo
Post by Gavin Lambert
Post by angelo
static int amcorefb_mmap(struct fb_info *info, struct vm_area_struct
*vma) {
No, that's not the right one. You don't need to implement that.
Post by angelo
Seems i miss the "get_unmapped_area/BDI_CAP_MAP_DIRECT) capability.
fbmem.c implements the main get_unmapped_area handler. In your framebuffer
driver you just need to either implement fb_get_unmapped_area or (easier,
but only works on nommu) set fix.smem_start and .smem_len correctly in your
fb_info.
--
Daniel Glöckner
2012-12-13 16:22:42 UTC
Permalink
Post by angelo
if (!(capabilities & BDI_CAP_MAP_DIRECT))
{
--> printk (KERN_INFO "ENODEV 2\n");
--> return -ENODEV;
}
So, as i explained, i cannot have BDI_CAP_MAP_DIRECT for this
architecture, and the mmap call using MAP_SHARED fails.
You can have BDI_CAP_MAP_DIRECT for this architecture.
You just need to define HAVE_ARCH_FB_UNMAPPED_AREA and implement
get_fb_unmapped_area. Take a look at the Blackfin implementation
of this function. It might work without modification on m68k-nommu.

Daniel
angelo
2012-12-13 18:30:54 UTC
Permalink
Dear Daniel,

many thanks,
Post by Daniel Glöckner
You can have BDI_CAP_MAP_DIRECT for this architecture.
You just need to define HAVE_ARCH_FB_UNMAPPED_AREA and implement
get_fb_unmapped_area. Take a look at the Blackfin implementation
of this function. It might work without modification on m68k-nommu.
yes, should be quite easy to do as you mentioned, but requires minimal
changes in the kernel, and i must keep the unofficial changes stored
here locally with the current tree.

Of course, i can send a patch to the list, but i am using kernel 2.6.x.
and also not sure it can be useful to anyone except me, so easy to be
unaccepted.

For this reasons, my idea is to setup 2 or 3 ioctl's at driver level:

- one to get memory address (direct memory map)
- one to update all the screen
- one to update a specific surface area

What do you think is better ?

Best Regards,
Angelo
Geert Uytterhoeven
2012-12-13 19:17:43 UTC
Permalink
Hi Angelo,
Post by angelo
Post by Daniel Glöckner
You can have BDI_CAP_MAP_DIRECT for this architecture.
You just need to define HAVE_ARCH_FB_UNMAPPED_AREA and implement
get_fb_unmapped_area. Take a look at the Blackfin implementation
of this function. It might work without modification on m68k-nommu.
yes, should be quite easy to do as you mentioned, but requires minimal
changes in the kernel, and i must keep the unofficial changes stored
here locally with the current tree.
Of course, i can send a patch to the list, but i am using kernel 2.6.x.
and also not sure it can be useful to anyone except me, so easy to be
unaccepted.
- one to get memory address (direct memory map)
That's a bit silly. If your fb_fix_screeninfo.smem_start is correctly filled in,
an application can already mmap /dev/mem using the smem_start offset.
That works even on real Linux with MMU.

On uClinux, the application can just write to the smem_start address,
without involving mmap(), right?

Gr{oetje,eeting}s,

Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert at linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
angelo
2012-12-13 20:48:31 UTC
Permalink
Dear Geert,

thanks, i tried your solution, works, i am able to write on the lcd
video memory using mmap() on /dev/mem, but there is still the issue of
refreshing the lcd i am driving.

I tried both msync() and munmap(), screen is not updated. But i can see
the changes on it if i write just after some chars with "echo" on /dev/fb0.

Any suggestion ?

Best Regards
Angelo
Post by Geert Uytterhoeven
Post by angelo
- one to get memory address (direct memory map)
That's a bit silly. If your fb_fix_screeninfo.smem_start is correctly filled in,
an application can already mmap /dev/mem using the smem_start offset.
That works even on real Linux with MMU.
On uClinux, the application can just write to the smem_start address,
without involving mmap(), right?
--
Gavin Lambert
2012-12-13 22:07:23 UTC
Permalink
Post by angelo
Seems that for my case (m68k-nommu) it is not defined, so none of the
get_unmapped_area will be called, in any place.
It doesn't need to be defined. fbmem.c implements get_unmapped_area
regardless (just slightly differently in each case). If you had actually
tried just setting smem_start as I said you should have seen it working.
Post by angelo
thanks, i tried your solution, works, i am able to write on the lcd
video memory using mmap() on /dev/mem, but there is still the issue of
refreshing the lcd i am driving.
Without an MMU, there is no way to get a callback when an application writes
directly to framebuffer memory. If your device requires you to take some
action beyond just updating the memory (eg. sending some kind of extra
command) then you will either need to use an ioctl to request a screen
refresh each time the app finishes drawing (which only works if you are in
control of the app's code) or to use some other mechanism to detect when the
framebuffer memory has been changed.

Another LCD driver I have creates a shadow copy of the framebuffer and has a
worker thread periodically compare the main framebuffer and the shadow
buffer; whenever it finds a difference it copies it to the shadow buffer and
updates the LCD. It's a bit crude, and it limits the frame rate to whatever
rate the worker thread runs at, but it works.
Daniel Glöckner
2012-12-13 23:24:50 UTC
Permalink
Post by Gavin Lambert
Post by angelo
Seems that for my case (m68k-nommu) it is not defined, so none of the
get_unmapped_area will be called, in any place.
It doesn't need to be defined. fbmem.c implements get_unmapped_area
regardless (just slightly differently in each case). If you had actually
tried just setting smem_start as I said you should have seen it working.
Are we talking about the same kernel tree?
Mainline had in 2.6.36.2 and to date still has

#ifdef HAVE_ARCH_FB_UNMAPPED_AREA
.get_unmapped_area = get_fb_unmapped_area,
#endif

in fbmem.c with no implementation of get_fb_unmapped_area in that file.

Daniel
Gavin Lambert
2012-12-14 00:10:05 UTC
Permalink
Post by Daniel Glöckner
Are we talking about the same kernel tree?
Mainline had in 2.6.36.2 and to date still has
No, I was looking at my local kernel tree for an old project, which is
considerably older. But looking at the official tree now, it looks like I
might have patched the get_unmapped_area support directly into there myself
at some point. It does work though.

I guess to properly support FB mmap, someone will have to add
HAVE_ARCH_FB_UNMAPPED_AREA support into arch/m68k, probably in a manner
similar to the bfin implementation. Or just provide a default !MMU
implementation the way I did.

Though just accessing smem_start directly works too -- but again, assumes
that you can rewrite the application.

angelo
2012-12-13 23:57:36 UTC
Permalink
Dear Lambert,

well i finally managed to use smem_start to access memory, and for
screen refresh i have to some ioctl, since i need to refresh some square
areas for sprites dislpay.

Many Thanks
Best Regards

Angelo
Post by Gavin Lambert
Post by angelo
Seems that for my case (m68k-nommu) it is not defined, so none of the
get_unmapped_area will be called, in any place.
It doesn't need to be defined. fbmem.c implements get_unmapped_area
regardless (just slightly differently in each case). If you had actually
tried just setting smem_start as I said you should have seen it working.
Post by angelo
thanks, i tried your solution, works, i am able to write on the lcd
video memory using mmap() on /dev/mem, but there is still the issue of
refreshing the lcd i am driving.
Without an MMU, there is no way to get a callback when an application writes
directly to framebuffer memory. If your device requires you to take some
action beyond just updating the memory (eg. sending some kind of extra
command) then you will either need to use an ioctl to request a screen
refresh each time the app finishes drawing (which only works if you are in
control of the app's code) or to use some other mechanism to detect when the
framebuffer memory has been changed.
Another LCD driver I have creates a shadow copy of the framebuffer and has a
worker thread periodically compare the main framebuffer and the shadow
buffer; whenever it finds a difference it copies it to the shadow buffer and
updates the LCD. It's a bit crude, and it limits the frame rate to whatever
rate the worker thread runs at, but it works.
--
Daniel Glöckner
2012-12-13 23:46:01 UTC
Permalink
Post by angelo
yes, should be quite easy to do as you mentioned, but requires minimal
changes in the kernel, and i must keep the unofficial changes stored
here locally with the current tree.
I don't see your problem with that. You are using a two years old
kernel. There is no point in trying to stay close to the official
release.
Post by angelo
- one to get memory address (direct memory map)
- one to update all the screen
- one to update a specific surface area
What do you think is better ?
Are you asking me if I favor a proprietary hack over the clean way?

As Gavin already pointed out, in cases like yours a popular solution is
to use a timer to periodically send updates to the LCD. If you can call
IOCTLs to update the screen, you might as well just use write instead
of mmap.

Daniel
Continue reading on narkive:
Loading...