The patch titled drivers-add-lcd-support update has been added to the -mm tree. Its filename is drivers-add-lcd-support-update.patch See http://www.zip.com.au/~akpm/linux/patches/stuff/added-to-mm.txt to find out what to do about this ------------------------------------------------------ Subject: drivers-add-lcd-support update From: Miguel Ojeda Sandonis <maxextreme@xxxxxxxxx> The patch to fix Greg's points. - Remove all about ioctls - Remove "Inited" and "Exited" noise. - Update Documentation, ioctl-number.txt and ABI * Don't fixed because Greg is trying to fix a problem with udev: - Use device_* instead of class_device_* - Use dev_* (as they need a struct device* parameter, returned by device_* functions) Signed-off-by: Miguel Ojeda Sandonis <maxextreme@xxxxxxxxx> Cc: Greg KH <greg@xxxxxxxxx> Signed-off-by: Andrew Morton <akpm@xxxxxxxx> --- Documentation/ABI/testing/cfag12864b | 74 +++++---- Documentation/auxdisplay/cfag12864b | 196 ++++++++++--------------- Documentation/ioctl-number.txt | 2 drivers/auxdisplay/auxlcdclass.c | 4 drivers/auxdisplay/cfag12864b.c | 103 +------------ drivers/auxdisplay/ks0108.c | 4 include/linux/cfag12864b.h | 10 - 7 files changed, 132 insertions(+), 261 deletions(-) diff -puN Documentation/ABI/testing/cfag12864b~drivers-add-lcd-support-update Documentation/ABI/testing/cfag12864b --- a/Documentation/ABI/testing/cfag12864b~drivers-add-lcd-support-update +++ a/Documentation/ABI/testing/cfag12864b @@ -1,50 +1,56 @@ What: drivers/auxdisplay/cfag12864b driver -Date: 2006-10-04 +Date: 2006-10-06 KernelVersion: 2.6.18 Contact: Miguel Ojeda Sandonis <maxextreme@xxxxxxxxx> Description: - The cfag12864b LCD driver defines two ways to communicate - with the lcd. + The cfag12864b LCD driver defines one way to communicate + with the lcd: - 1. Use seek and write syscalls. Bytes written appear on + 1. Use seek and write syscalls. Bytes written appear on the screen without any formatting on the position pointed by the file offset. - It is hardware dependent. It should be use to modify + It is hardware dependent. It should be used to modify specific display's pixels to achieve higher refreshing rates. - 2. Use ioctl syscall. The magic number is 0xFF. - There are four ioctls: + This method allows you to change each byte of the device, + so you can achieve a higher update rate updating only the + pixels you are going to change. + + The device size is 1024 == CFAG12864B_SIZE. + + You can write and seek the device. The first 512 bytes + write to the first k0108 controller (left display half) and + the last 512 bytes write to the second ks0108 controller + (right display half). + + Each controller is divided into 8 pages. + Each page has 64 addresses (bytes). + + Controller 0 Controller 1 + _________________________ + Page 0 --> |____________|____________| + Page 1 --> |____________|____________| + Page 2 --> |____________|____________| + Page 3 --> |____________|____________| + Page 4 --> |____________|____________| + Page 5 --> |____________|____________| + Page 6 --> |____________|____________| + Page 7 --> |____________|____________| + <--- 64 ---> + Addresses + + The beggining of the file is at controller 0, page 0, + address 0. - 2.0. off _IO(0xFF,0) + When you reach the position 64, you are writing to the + same controller, next page, address 0. - Power off display. + After 512 bytes written, you are writing to the second + controller, starting at page 0 and address 0 again. - It doesn't clear the display. - It doesn't stop the controllers. - - 2.1. on _IO(0xFF,1) - - Power on display. - - 2.2. clear _IO(0xFF,2) - - Clear the display. - - 2.3. format _IOW(0xFF,3,void *) - - Read the given buffer, transform it to the hardware - dependent format and show it on the screen. - - The argument must point to a userspace buffer of - size 128*64 bytes (the display's size). - - Each buffer's byte (unsigned) represent a pixel: - 0 = pixel will turn off - >0 = pixel will turn on - - For more information and examples, see - Documentation/auxdisplay/cfag12864b + For more information and examples, see + Documentation/auxdisplay/cfag12864b Users: diff -puN Documentation/auxdisplay/cfag12864b~drivers-add-lcd-support-update Documentation/auxdisplay/cfag12864b --- a/Documentation/auxdisplay/cfag12864b~drivers-add-lcd-support-update +++ a/Documentation/auxdisplay/cfag12864b @@ -4,7 +4,7 @@ License: GPLv2 Author & Maintainer: Miguel Ojeda Sandonis <maxextreme@xxxxxxxxx> -Date: 2006-10-04 +Date: 2006-10-06 @@ -16,8 +16,7 @@ Date: 2006-10-04 2. DEVICE INFORMATION 3. WIRING 4. USER-SPACE PROGRAMMING - 4.1. ioctl and a 128*64 boolean matrix - 4.2. Direct writing + 4.1. Direct writing 5. USEFUL FILES 5.1. cfag12864b.h 5.2. bmpwriter.h @@ -76,72 +75,12 @@ Include a copy of the provided header: Open the device for writing, /dev/cfag12864b0: - int fd = open("/dev/cfag12864b0",O_WRONLY); + FILE * fdisplay = fopen("/dev/cfag12864b0","wb"); -Then use simple ioctl calls to control it: +For writing to the display, you have the following options: - ioctl(fdisplay,CFAG12864B_IOCOFF); /* Turn off (don't clear) */ - ioctl(fdisplay,CFAG12864B_IOCON); /* Turn on */ - ioctl(fdisplay,CFAG12864B_IOCCLEAR); /* Clear the display */ -For writing to the display, you have two options: - - -4.1. ioctl & 128*64 boolean matrix -------------------------------------------------------- - -This method is easier, but you have to update the entire display -each time you want to change it. - -Just calling the ioctl: - - ioctl(fdisplay, CFAG12864B_IOCFORMAT, Buffer); - -Your drawing should appear on the display. - -Note: - - CFAG12864B_FORMATSIZE == - CFAG12864B_WIDTH * CFAG12864B_HEIGHT == - 128 * 64 - -The buffer should be a 128*64 unsigned char array: - - unsigned char Buffer[CFAG12864B_FORMATSIZE]; - -To fill this buffer, you have several ways. Examples: - -a) - Fill a bidimensional 128x64 buffer (each byte representing a pixel - on the display), copy it to the first one and call the ioctl: - - unsigned char Buffer[CFAG12864B_FORMATSIZE]; - unsigned char MyDrawing[CFAG12864B_WIDTH][CFAG12864B_HEIGHT]; - - /* Create drawing ... */ - - for(i = 0; i < CFAG12864B_WIDTH; i++) - for(j = 0; j < CFAG12864B_HEIGHT; j++) - Buffer[i + j * CFAG12864B_WIDTH] = MyDrawing[i][j]; - - ioctl(fdisplay, CFAG12864B_IOCFORMAT, Buffer); - -b) - Create helper functions/macros to change a specific bit - in the final buffer. This way you don't need an extra buffer, - and it is faster. When you are ready, call the ioctl: - - #define SET(x,y) Buffer[x + y * CFAG12864B_WIDTH] = 1; - #define UNSET(x,y) Buffer[x + y * CFAG12864B_WIDTH] = 0; - - unsigned char Buffer[CFAG12864B_FORMATSIZE]; - - /* Create drawing ... */ - - ioctl(fdisplay, CFAG12864B_IOCFORMAT, Buffer); - - -4.2. Direct writing +4.1. Direct writing ------------------- This method allows you to change each byte of the device, @@ -156,37 +95,55 @@ write to the second ks0108 controller (r Each controller is divided into 8 pages. Each page has 64 bytes. - Controller 0 Controller 1 - _________________________ -Page 0 |____________|____________| -Page 1 |____________|____________| -Page 2 |____________|____________| -Page 3 |____________|____________| -Page 4 |____________|____________| -Page 5 |____________|____________| -Page 6 |____________|____________| -Page 7 |____________|____________| - <--- 64 ---> + Controller 0 Controller 1 + _________________________ +Page 0 --> |____________|____________| +Page 1 --> |____________|____________| +Page 2 --> |____________|____________| +Page 3 --> |____________|____________| +Page 4 --> |____________|____________| +Page 5 --> |____________|____________| +Page 6 --> |____________|____________| +Page 7 --> |____________|____________| + <--- 64 ---> + +The beggining of the file is at controller 0, page 0, address 0. + +When you reach the position 64, you are writing to the same controller, +next page, address 0. + +After 512 bytes written, you are writing to the second controller, starting +at page 0 and address 0 again. You will understand how the device work executing some commands: - # echo -n A > /dev/cfag12864b0 - # echo -n a > /dev/cfag12864b0 - # echo AAAAAA > /dev/cfag12864b0 - # echo 000000 > /dev/cfag12864b0 - # echo Hello world! > /dev/cfag12864b0 - # echo Hello world! Hello world! > /dev/cfag12864b0 + Write some stuff: + # echo -n A > /dev/cfag12864b0 + # echo -n a > /dev/cfag12864b0 + # echo AAAAAA > /dev/cfag12864b0 + # echo 000000 > /dev/cfag12864b0 + # echo Hello world! > /dev/cfag12864b0 + # echo Hello world! Hello world! > /dev/cfag12864b0 + + Clear the display: + # cat /dev/zero > /dev/cfag12864b0 + + Fill with random values: + # cat /dev/random > /dev/cfag12864b0 + ... After you understand it, code your functions to change specific bytes. -Use write() and lseek() system calls, like: +Use functions like fwrite() and fseek() to control the device, like: + + fseek(fdisplay, ipage * CFAG12864B_HEIGHT, SEEK_SET); + fseek(fdisplay, icontroller * CFAG12864B_SIZE / 2, SEEK_SET); - lseek(fdisplay, ipage * CFAG12864B_HEIGHT, SEEK_SET); - lseek(fdisplay, icontroller * CFAG12864B_SIZE / 2, SEEK_SET); + fwrite(bufpage, 1, CFAG12864B_HEIGHT, fdisplay); + fwrite(bufcontroller, 1, CFAG12864B_SIZE / 2, fdisplay); + fwrite(bufdisplay, 1, CFAG12864B_SIZE, fdisplay); - write(fdisplay, bufpage, CFAG12864B_HEIGHT); - write(fdisplay, bufcontroller, CFAG12864B_SIZE / 2); - write(fdisplay, bufdisplay, CFAG12864B_SIZE); + ... --------------- @@ -212,20 +169,11 @@ You can use a copy of this header in you #ifndef _CFAG12864B_H_ #define _CFAG12864B_H_ -#include <sys/ioctl.h> - #define CFAG12864B_WIDTH (128) #define CFAG12864B_HEIGHT (64) #define CFAG12864B_FORMATSIZE ((CFAG12864B_WIDTH)*(CFAG12864B_HEIGHT)) #define CFAG12864B_SIZE (1024) -#define CFAG12864B_IOC_MAGIC (0xFF) - -#define CFAG12864B_IOCOFF (_IO((CFAG12864B_IOC_MAGIC),0)) -#define CFAG12864B_IOCON (_IO((CFAG12864B_IOC_MAGIC),1)) -#define CFAG12864B_IOCCLEAR (_IO((CFAG12864B_IOC_MAGIC),2)) -#define CFAG12864B_IOCFORMAT (_IOW((CFAG12864B_IOC_MAGIC),3,void *)) - #endif // _CFAG12864B_H_ --- @@ -271,21 +219,33 @@ union dword #define Bit(n) ((unsigned char)(1<<(n))) -void BMP2Format( - unsigned char _Src[BMP_SIZE], - unsigned char _Dest[CFAG12864B_FORMATSIZE]) +void BMP2Format(unsigned char * src, unsigned char * dest) { - const unsigned int Width = CFAG12864B_WIDTH; - const unsigned int Height = CFAG12864B_HEIGHT; - const unsigned int Bits = 8; + const unsigned int width = CFAG12864B_WIDTH; + const unsigned int height = CFAG12864B_HEIGHT; + const unsigned int bits = 8; + + unsigned int y,x,bit; + + for(y=0; y<height; ++y) + for(x=0; x<width/bits; ++x) + for(bit=0; bit<bits; ++bit) + dest[x * bits + bit + (height - y - 1) * width] = + src[y * width / bits + x] & Bit(bits - bit - 1) ? 0 : 1; +} - unsigned int Y,X,Bit; +void Format2cfag12864b(unsigned char * src, unsigned char * dest) +{ + unsigned short controller,page,address,bit; - for(Y=0; Y<Height; ++Y) - for(X=0; X<Width/Bits; ++X) - for(Bit=0; Bit<Bits; ++Bit) - _Dest[X * Bits + Bit + (Height - Y - 1) * Width] = - _Src[Y * Width / Bits + X] & Bit(Bits - Bit - 1) ? 0 : 1; + for(controller=0; controller<2; ++controller) + for(page=0; page<8; ++page) + for(address=0; address<64; ++address) { + dest[(controller*8+page)*64+address]=0; + for(bit=0; bit<8; ++bit) + if(src[controller*64+address+(page*8+bit)*CFAG12864B_WIDTH]) + dest[(controller*8+page)*64+address]|=Bit(bit); + } } int main(int argc, char * argv[]) @@ -303,8 +263,9 @@ int main(int argc, char * argv[]) unsigned char Buffer_BMP[BMP_SIZE]; unsigned char Buffer_Matrix[CFAG12864B_FORMATSIZE]; + unsigned char Buffer_cfag12864b[CFAG12864B_SIZE]; - int fdisplay; + FILE * fdisplay; FILE * fbmp; // Check args @@ -383,18 +344,21 @@ int main(int argc, char * argv[]) // Transform BMP data to 2D matrix BMP2Format(Buffer_BMP,Buffer_Matrix); + // Transform 2D matrix to cfag12864b RAW format + Format2cfag12864b(Buffer_cfag12864b); + // Open file - fdisplay = open(argv[1],O_WRONLY); - if(fdisplay < 0) { + fdisplay = fopen(argv[1],"wb"); + if(fdisplay == NULL) { printf("%s: Can't open %s\n", argv[0], argv[1]); return -9; } - // Send matrix - ioctl(fdisplay,CFAG12864B_IOCFORMAT,Buffer_Matrix); + // Send RAW image + fwrite(Buffer_cfag12864b,1,CFAG12864B_SIZE,fdisplay); // Close file - close(fdisplay); + fclose(fdisplay); return 0; } diff -puN Documentation/ioctl-number.txt~drivers-add-lcd-support-update Documentation/ioctl-number.txt --- a/Documentation/ioctl-number.txt~drivers-add-lcd-support-update +++ a/Documentation/ioctl-number.txt @@ -191,5 +191,3 @@ Code Seq# Include File Comments <mailto:aherrman@xxxxxxxxxx> 0xF3 00-3F video/sisfb.h sisfb (in development) <mailto:thomas@xxxxxxxxxxxxxxxx> -0xFF 00-1F linux/cfag12864b.h cfag12864b Auxiliary LCD driver - <mailto:maxextreme@xxxxxxxxx> diff -puN drivers/auxdisplay/auxlcdclass.c~drivers-add-lcd-support-update drivers/auxdisplay/auxlcdclass.c --- a/drivers/auxdisplay/auxlcdclass.c~drivers-add-lcd-support-update +++ a/drivers/auxdisplay/auxlcdclass.c @@ -55,8 +55,6 @@ static int __init auxlcdclass_init(void) goto none; } - printk(KERN_DEBUG AUXLCDCLASS_NAME ": Inited\n"); - return 0; none: @@ -66,8 +64,6 @@ none: static void __exit auxlcdclass_exit(void) { class_destroy(auxlcdclass_class); - - printk(KERN_DEBUG AUXLCDCLASS_NAME ": Exited\n"); } module_init(auxlcdclass_init); diff -puN drivers/auxdisplay/cfag12864b.c~drivers-add-lcd-support-update drivers/auxdisplay/cfag12864b.c --- a/drivers/auxdisplay/cfag12864b.c~drivers-add-lcd-support-update +++ a/drivers/auxdisplay/cfag12864b.c @@ -6,7 +6,7 @@ * Depends: auxlcdclass ks0108 * * Author: Copyright (C) Miguel Ojeda Sandonis <maxextreme@xxxxxxxxx> - * Date: 2006-10-04 + * Date: 2006-10-06 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -45,10 +45,9 @@ static const unsigned int cfag12864b_fir static const unsigned int cfag12864b_ndevices = 1; static int cfag12864b_major; static int cfag12864b_minor; -static dev_t cfag12864b_device; +static dev_t cfag12864b_devt; struct cdev cfag12864b_chardevice; static unsigned char * cfag12864b_buffer; -static unsigned char * cfag12864b_formatbuffer; DECLARE_MUTEX(cfag12864b_mutex); /* @@ -359,31 +358,6 @@ EXPORT_SYMBOL_GPL(cfag12864b_write); EXPORT_SYMBOL_GPL(cfag12864b_format); /* - * cfag12864b ioctls (don't lock because ioctl fop do) - */ - -static int cfag12864b_fopioctlformat(void __user *arg) -{ - int result; - int ret = -ENOTTY; - - result = copy_from_user(cfag12864b_formatbuffer, arg, - sizeof(unsigned char) * CFAG12864B_MATRIXSIZE); - if (result != 0) { - printk(KERN_ERR CFAG12864B_NAME ": FOP ioctl: ERROR: " - "can't copy memory from user\n"); - goto none; - } - - cfag12864b_format_nolock(cfag12864b_formatbuffer); - - ret = 0; - -none: - return ret; -} - -/* * cfag12864b_fops (do lock) */ @@ -453,47 +427,11 @@ none: return ret; } -static int cfag12864b_fopioctl(struct inode *inode, struct file *filp, - unsigned int cmd, unsigned long arg) -{ - int ret = -ENOTTY; - - if (down_interruptible(&cfag12864b_mutex)) - return -ERESTARTSYS; - - if (_IOC_TYPE(cmd) != CFAG12864B_IOC_MAGIC) - goto none; - if (_IOC_NR(cmd) > CFAG12864B_IOC_MAXNR) - goto none; - - switch (cmd) { - case CFAG12864B_IOCON: - cfag12864b_on_nolock(); - ret = 0; - break; - case CFAG12864B_IOCOFF: - cfag12864b_off_nolock(); - ret = 0; - break; - case CFAG12864B_IOCCLEAR: - cfag12864b_clear_nolock(); - ret = 0; - break; - case CFAG12864B_IOCFORMAT: - ret = cfag12864b_fopioctlformat((void __user *)arg); - } - -none: - up(&cfag12864b_mutex); - return ret; -} - static const struct file_operations cfag12864b_fops = { .owner = THIS_MODULE, .llseek = cfag12864b_fopseek, .write = cfag12864b_fopwrite, - .ioctl = cfag12864b_fopioctl, }; /* @@ -514,27 +452,18 @@ static int __init cfag12864b_init(void) goto none; } - cfag12864b_formatbuffer = kmalloc(sizeof(unsigned char) * CFAG12864B_MATRIXSIZE, - GFP_KERNEL); - if (cfag12864b_formatbuffer == NULL) { - printk(KERN_ERR CFAG12864B_NAME ": ERROR: " - "can't alloc format buffer (%i bytes)\n", CFAG12864B_MATRIXSIZE); - ret = -ENOMEM; - goto bufferalloced; - } - - result = alloc_chrdev_region(&cfag12864b_device, cfag12864b_firstminor, + result = alloc_chrdev_region(&cfag12864b_devt, cfag12864b_firstminor, cfag12864b_ndevices, CFAG12864B_NAME); if (result < 0) { printk(KERN_ERR CFAG12864B_NAME ": ERROR: " "can't alloc the char device region\n"); ret = result; - goto formatbufferalloced; + goto bufferalloced; } - cfag12864b_major = MAJOR(cfag12864b_device); + cfag12864b_major = MAJOR(cfag12864b_devt); cfag12864b_minor = cfag12864b_firstminor; - cfag12864b_device = MKDEV(cfag12864b_major, cfag12864b_minor); + cfag12864b_devt = MKDEV(cfag12864b_major, cfag12864b_minor); cfag12864b_clear_nolock(); cfag12864b_on_nolock(); @@ -542,7 +471,7 @@ static int __init cfag12864b_init(void) cdev_init(&cfag12864b_chardevice, &cfag12864b_fops); cfag12864b_chardevice.owner = THIS_MODULE; cfag12864b_chardevice.ops = &cfag12864b_fops; - result = cdev_add(&cfag12864b_chardevice, cfag12864b_device, + result = cdev_add(&cfag12864b_chardevice, cfag12864b_devt, cfag12864b_ndevices); if (result < 0) { printk(KERN_ERR CFAG12864B_NAME ": ERROR: " @@ -551,26 +480,21 @@ static int __init cfag12864b_init(void) goto regionalloced; } - if (class_device_create(auxlcdclass_class, NULL, cfag12864b_device, NULL, + if (class_device_create(auxlcdclass_class, NULL, cfag12864b_devt, NULL, "cfag12864b%d", cfag12864b_minor) == NULL) { printk(KERN_ERR CFAG12864B_NAME ": ERROR: " - "unable to create a device for the LCD class\n"); + "unable to create a class device for the auxlcdclass\n"); ret = -EINVAL; goto cdevadded; } - printk(KERN_DEBUG CFAG12864B_NAME ": Inited\n"); - return 0; cdevadded: cdev_del(&cfag12864b_chardevice); regionalloced: - unregister_chrdev_region(cfag12864b_device, cfag12864b_ndevices); - -formatbufferalloced: - kfree(cfag12864b_formatbuffer); + unregister_chrdev_region(cfag12864b_devt, cfag12864b_ndevices); bufferalloced: kfree(cfag12864b_buffer); @@ -583,13 +507,10 @@ static void __exit cfag12864b_exit(void) { cfag12864b_off_nolock(); - class_device_destroy(auxlcdclass_class, cfag12864b_device); + class_device_destroy(auxlcdclass_class, cfag12864b_devt); cdev_del(&cfag12864b_chardevice); - unregister_chrdev_region(cfag12864b_device, cfag12864b_ndevices); - kfree(cfag12864b_formatbuffer); + unregister_chrdev_region(cfag12864b_devt, cfag12864b_ndevices); kfree(cfag12864b_buffer); - - printk(KERN_DEBUG CFAG12864B_NAME ": Exited\n"); } module_init(cfag12864b_init); diff -puN drivers/auxdisplay/ks0108.c~drivers-add-lcd-support-update drivers/auxdisplay/ks0108.c --- a/drivers/auxdisplay/ks0108.c~drivers-add-lcd-support-update +++ a/drivers/auxdisplay/ks0108.c @@ -143,8 +143,6 @@ static int __init ks0108_init(void) goto registered; } - printk(KERN_DEBUG KS0108_NAME ": Inited - ks0108_port=0x%X ks0108_delay=%i\n", - ks0108_port, ks0108_delay); return 0; registered: @@ -158,8 +156,6 @@ static void __exit ks0108_exit(void) { parport_release(ks0108_pardevice); parport_unregister_device(ks0108_pardevice); - - printk(KERN_DEBUG KS0108_NAME ": Exited\n"); } module_init(ks0108_init); diff -puN include/linux/cfag12864b.h~drivers-add-lcd-support-update include/linux/cfag12864b.h --- a/include/linux/cfag12864b.h~drivers-add-lcd-support-update +++ a/include/linux/cfag12864b.h @@ -26,8 +26,6 @@ #ifndef _CFAG12864B_H_ #define _CFAG12864B_H_ -#include <linux/ioctl.h> - #define CFAG12864B_WIDTH (128) #define CFAG12864B_HEIGHT (64) #define CFAG12864B_MATRIXSIZE ((CFAG12864B_WIDTH) * (CFAG12864B_HEIGHT)) @@ -39,14 +37,6 @@ (CFAG12864B_PAGES) * \ (CFAG12864B_ADDRESSES)) -#define CFAG12864B_IOC_MAGIC (0xFF) -#define CFAG12864B_IOC_MAXNR (0x03) - -#define CFAG12864B_IOCOFF (_IO((CFAG12864B_IOC_MAGIC),0)) -#define CFAG12864B_IOCON (_IO((CFAG12864B_IOC_MAGIC),1)) -#define CFAG12864B_IOCCLEAR (_IO((CFAG12864B_IOC_MAGIC),2)) -#define CFAG12864B_IOCFORMAT (_IOW((CFAG12864B_IOC_MAGIC),3,void *)) - extern void cfag12864b_on(void); extern void cfag12864b_off(void); extern void cfag12864b_clear(void); _ Patches currently in -mm which might be from maxextreme@xxxxxxxxx are drivers-add-lcd-support.patch drivers-add-lcd-support-update.patch - To unsubscribe from this list: send the line "unsubscribe mm-commits" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html