diff options
author | Richard Purdie <richard@openedhand.com> | 2008-01-20 17:03:32 +0000 |
---|---|---|
committer | Richard Purdie <richard@openedhand.com> | 2008-01-20 17:03:32 +0000 |
commit | 53d19e1c1ffda8e7724d9d6658e3bb9acde758a7 (patch) | |
tree | 11a7541bc58ef7c90c75984de24b2f6a518506a0 | |
parent | 388339b8f62705597453e5d627724c8dae90ced8 (diff) | |
download | openembedded-core-53d19e1c1ffda8e7724d9d6658e3bb9acde758a7.tar.gz openembedded-core-53d19e1c1ffda8e7724d9d6658e3bb9acde758a7.tar.bz2 openembedded-core-53d19e1c1ffda8e7724d9d6658e3bb9acde758a7.zip |
linux-rp: Tidy up .bb file and clean up unused patches
git-svn-id: https://svn.o-hand.com/repos/poky/trunk@3557 311d38ba-8fff-0310-9ca6-ca027cbcb966
22 files changed, 3689 insertions, 18131 deletions
diff --git a/meta/packages/linux/linux-rp-2.6.23+2.6.24-rc8/connectplus-prevent-oops-HACK.patch b/meta/packages/linux/linux-rp-2.6.23+2.6.24-rc8/connectplus-prevent-oops-HACK.patch new file mode 100644 index 0000000000..b5439c62e7 --- /dev/null +++ b/meta/packages/linux/linux-rp-2.6.23+2.6.24-rc8/connectplus-prevent-oops-HACK.patch @@ -0,0 +1,17 @@ +Index: linux-2.6.21/drivers/net/wireless/hostap/hostap_hw.c +=================================================================== +--- linux-2.6.21.orig/drivers/net/wireless/hostap/hostap_hw.c 2007-07-07 12:45:39.000000000 +0100 ++++ linux-2.6.21/drivers/net/wireless/hostap/hostap_hw.c 2007-07-07 12:47:30.000000000 +0100 +@@ -2666,6 +2666,12 @@ + iface = netdev_priv(dev); + local = iface->local; + ++ if(dev->base_addr == 0) ++ { ++ printk(KERN_DEBUG "%s: IRQ before base_addr set\n", dev->name); ++ return IRQ_HANDLED; ++ } ++ + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INTERRUPT, 0, 0); + + if (local->func->card_present && !local->func->card_present(local)) { diff --git a/meta/packages/linux/linux-rp-2.6.23+2.6.24-rc8/hx2750_base-r31.patch b/meta/packages/linux/linux-rp-2.6.23+2.6.24-rc8/hx2750_base-r31.patch deleted file mode 100644 index 5d58bcf55d..0000000000 --- a/meta/packages/linux/linux-rp-2.6.23+2.6.24-rc8/hx2750_base-r31.patch +++ /dev/null @@ -1,1094 +0,0 @@ ---- - arch/arm/mach-pxa/Kconfig | 10 - arch/arm/mach-pxa/Makefile | 1 - arch/arm/mach-pxa/hx2750.c | 450 ++++++++++++++++++++++++++++++++++++++ - arch/arm/mach-pxa/hx2750_test.c | 433 ++++++++++++++++++++++++++++++++++++ - arch/arm/mach-pxa/pm.c | 5 - arch/arm/mach-pxa/pxa25x.c | 4 - arch/arm/mach-pxa/pxa27x.c | 4 - include/asm-arm/arch-pxa/hx2750.h | 90 +++++++ - 8 files changed, 995 insertions(+), 2 deletions(-) - ---- /dev/null -+++ linux-2.6.24-rc1/include/asm-arm/arch-pxa/hx2750.h -@@ -0,0 +1,90 @@ -+/* -+ * Hardware specific definitions for iPAQ hx2750 -+ * -+ * Copyright 2005 Openedhand Ltd. -+ * -+ * Author: Richard Purdie <richard@o-hand.com> -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ * -+ */ -+#ifndef __ASM_ARCH_HX2750_H -+#define __ASM_ARCH_HX2750_H 1 -+ -+/* -+ * HX2750 (Non Standard) GPIO Definitions -+ */ -+ -+#define HX2750_GPIO_KEYPWR (0) /* Power button */ -+#define HX2750_GPIO_BATTCOVER1 (9) /* Battery Cover Switch */ -+#define HX2750_GPIO_CF_IRQ (11) /* CF IRQ? */ -+#define HX2750_GPIO_USBCONNECT (12) /* USB Connected? */ -+#define HX2750_GPIO_CF_DETECT (13) /* CF Card Detect? */ -+#define HX2750_GPIO_EXTPWR (14) /* External Power Detect */ -+#define HX2750_GPIO_BATLVL (15) /* Battery Level Detect */ -+#define HX2750_GPIO_CF_PWR (17) /* CF Power? */ -+#define HX2750_GPIO_SR_STROBE (18) /* Shift Register Strobe */ -+#define HX2750_GPIO_CHARGE (22) /* Charging Enable (active low) */ -+#define HX2750_GPIO_TSC2101_SS (24) /* TSC2101 SS# */ -+#define HX2750_GPIO_BTPWR (27) /* Bluetooth Power? */ -+#define HX2750_GPIO_BATTCOVER2 (33) /* Battery Cover Switch */ -+#define HX2750_GPIO_SD_DETECT (38) /* MMC Card Detect */ -+#define HX2750_GPIO_SR_CLK1 (52) /* Shift Register Clock */ -+#define HX2750_GPIO_SR_CLK2 (53) -+#define HX2750_GPIO_CF_WIFIIRQ (54) /* CF Wifi IRQ? */ -+#define HX2750_GPIO_GPIO_DIN (88) /* Shift Register Data */ -+#define HX2750_GPIO_KEYLEFT (90) /* Left button */ -+#define HX2750_GPIO_KEYRIGHT (91) /* Right button */ -+#define HX2750_GPIO_KEYCAL (93) /* Calander button */ -+#define HX2750_GPIO_KEYTASK (94) /* Task button */ -+#define HX2750_GPIO_KEYSIDE (95) /* Side button */ -+#define HX2750_GPIO_KEYENTER (96) /* Enter Button*/ -+#define HX2750_GPIO_KEYCON (97) /* Contacts button */ -+#define HX2750_GPIO_KEYMAIL (98) /* Mail button */ -+#define HX2750_GPIO_BIOPWR (99) /* BIO Reader Power? */ -+#define HX2750_GPIO_KEYUP (100) /* Up button */ -+#define HX2750_GPIO_KEYDOWN (101) /* Down button*/ -+#define HX2750_GPIO_SD_READONLY (103) /* MMC/SD Write Protection */ -+#define HX2750_GPIO_LEDMAIL (106) /* Green Mail LED */ -+#define HX2750_GPIO_HP_JACK (108) /* Head Phone Jack Present */ -+#define HX2750_GPIO_PENDOWN (117) /* Touch Screen Pendown */ -+ -+ -+//#define HX2750_GPIO_ () /* */ -+ -+/* -+ * HX2750 Interrupts -+ */ -+#define HX2750_IRQ_GPIO_EXTPWR IRQ_GPIO(HX2750_GPIO_EXTPWR) -+#define HX2750_IRQ_GPIO_SD_DETECT IRQ_GPIO(HX2750_GPIO_SD_DETECT) -+#define HX2750_IRQ_GPIO_PENDOWN IRQ_GPIO(HX2750_GPIO_PENDOWN) -+#define HX2750_IRQ_GPIO_CF_DETECT IRQ_GPIO(HX2750_GPIO_CF_DETECT) -+#define HX2750_IRQ_GPIO_CF_IRQ IRQ_GPIO(HX2750_GPIO_CF_IRQ) -+#define HX2750_IRQ_GPIO_CF_WIFIIRQ IRQ_GPIO(HX2750_GPIO_CF_WIFIIRQ) -+ -+/* -+ * HX2750 Extra GPIO Definitions -+ */ -+ -+#define HX2750_EGPIO_WIFI_PWR (1 << 11) /* Wifi power */ -+#define HX2750_EGPIO_LCD_PWR (1 << 10) /* LCD Power */ -+#define HX2750_EGPIO_BL_PWR (1 << 9) /* Backlight LED Power */ -+#define HX2750_EGPIO_8 (1 << 8) /* */ -+#define HX2750_EGPIO_7 (1 << 7) /* */ -+#define HX2750_EGPIO_SD_PWR (1 << 6) /* SD Power */ -+#define HX2750_EGPIO_TSC_PWR (1 << 5) /* TS Power */ -+#define HX2750_EGPIO_CF0_RESET (1 << 4) /* CF0 Reset */ -+#define HX2750_EGPIO_CF1_RESET (1 << 3) /* CF1 Reset */ -+#define HX2750_EGPIO_2 (1 << 2) /* Power/Red LED Related?*/ -+#define HX2750_EGPIO_1 (1 << 1) /* Power/Red LED Related?*/ -+#define HX2750_EGPIO_PWRLED (1 << 0) /* Power/Red LED Related?*/ -+ -+ -+void hx2750_set_egpio(unsigned int gpio); -+void hx2750_clear_egpio(unsigned int gpio); -+ -+ -+#endif /* __ASM_ARCH_HX2750_H */ -+ ---- linux-2.6.24-rc1.orig/arch/arm/mach-pxa/Makefile -+++ linux-2.6.24-rc1/arch/arm/mach-pxa/Makefile -@@ -22,6 +22,7 @@ - obj-$(CONFIG_MACH_POODLE) += poodle.o corgi_ssp.o - obj-$(CONFIG_MACH_TOSA) += tosa.o - obj-$(CONFIG_MACH_EM_X270) += em-x270.o -+obj-$(CONFIG_MACH_HX2750) += hx2750.o hx2750_test.o - - ifeq ($(CONFIG_MACH_ZYLONITE),y) - obj-y += zylonite.o ---- /dev/null -+++ linux-2.6.24-rc1/arch/arm/mach-pxa/hx2750_test.c -@@ -0,0 +1,433 @@ -+/* -+ * HP iPAQ hx2750 Test Development Code -+ * -+ * Copyright 2005 Openedhand Ltd. -+ * -+ * Author: Richard Purdie <richard@o-hand.com> -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ * -+ */ -+ -+#include <linux/init.h> -+#include <linux/kernel.h> -+#include <linux/ioport.h> -+#include <linux/device.h> -+#include <linux/input.h> -+#include <linux/delay.h> -+#include <linux/interrupt.h> -+ -+#include <asm/mach-types.h> -+#include <asm/hardware.h> -+#include <asm/mach/arch.h> -+ -+#include <asm/arch/hx2750.h> -+#include <asm/arch/pxa-regs.h> -+ -+ -+static int prodval; -+ -+/* -+ * Sysfs functions -+ */ -+static ssize_t test1_show(struct device *dev, char *buf) -+{ -+ unsigned long rp; -+ -+ asm ("mrc p15, 0, %0, cr1, cr0;\n" :"=r"(rp) ); -+ printk("%lx\n",rp); -+ -+ asm ("mrc p15, 0, %0, cr1, cr1;\n" :"=r"(rp) ); -+ printk("%lx\n",rp); -+ -+ asm ("mrc p15, 0, %0, cr2, cr0;\n" :"=r"(rp) ); -+ printk("%lx\n",rp); -+ -+ asm ("mrc p15, 0, %0, cr3, cr0;\n" :"=r"(rp) ); -+ printk("%lx\n",rp); -+ -+ asm ("mrc p15, 0, %0, cr13, cr0;\n" :"=r"(rp) ); -+ printk("%lx\n",rp); -+ -+ return sprintf(buf, "%d\n",prodval); -+} -+extern void tsc2101_print_miscdata(struct device *dev); -+extern struct platform_device tsc2101_device; -+ -+static ssize_t test1_store(struct device *dev, const char *buf, size_t count) -+{ -+ prodval = simple_strtoul(buf, NULL, 10); -+ -+ tsc2101_print_miscdata(&tsc2101_device.dev); -+ -+ return count; -+} -+ -+static DEVICE_ATTR(test1, 0644, test1_show, test1_store); -+ -+static ssize_t test2_show(struct device *dev, char *buf) -+{ -+ printk(KERN_ERR "SSCR0_P2: %08x\n", SSCR0_P2); -+ printk(KERN_ERR "SSCR1_P2: %08x\n", SSCR1_P2); -+ printk(KERN_ERR "SSSR_P2: %08x\n", SSSR_P2); -+ printk(KERN_ERR "SSITR_P2: %08x\n", SSITR_P2); -+ printk(KERN_ERR "SSDR_P2: %08x\n", SSDR_P2); -+ printk(KERN_ERR "SSTO_P2: %08x\n", SSTO_P2); -+ printk(KERN_ERR "SSPSP_P2: %08x\n", SSPSP_P2); -+ -+ hx2750_ssp_init2(); -+ -+ printk(KERN_ERR "SSCR0_P2: %08x\n", SSCR0_P2); -+ printk(KERN_ERR "SSCR1_P2: %08x\n", SSCR1_P2); -+ printk(KERN_ERR "SSSR_P2: %08x\n", SSSR_P2); -+ printk(KERN_ERR "SSITR_P2: %08x\n", SSITR_P2); -+ printk(KERN_ERR "SSDR_P2: %08x\n", SSDR_P2); -+ printk(KERN_ERR "SSTO_P2: %08x\n", SSTO_P2); -+ printk(KERN_ERR "SSPSP_P2: %08x\n", SSPSP_P2); -+ -+ return sprintf(buf, "%d\n",0); -+} -+ -+static DEVICE_ATTR(test2, 0444, test2_show, NULL); -+ -+extern unsigned int hx2750_egpio_current; -+ -+static ssize_t setegpio_show(struct device *dev, char *buf) -+{ -+ return sprintf(buf, "%x\n",hx2750_egpio_current); -+} -+ -+static ssize_t setegpio_store(struct device *dev, const char *buf, size_t count) -+{ -+ unsigned int val = simple_strtoul(buf, NULL, 10); -+ -+ hx2750_set_egpio(1 << val); -+ -+ return count; -+} -+ -+static DEVICE_ATTR(setegpio, 0644, setegpio_show, setegpio_store); -+ -+static ssize_t clregpio_show(struct device *dev, char *buf) -+{ -+ return sprintf(buf, "%x\n",hx2750_egpio_current); -+} -+ -+static ssize_t gpio_show(struct device *dev, char *buf) -+{ -+ int i; -+ -+ printk(KERN_ERR "GPIO# D S A INTER\n"); -+ -+ for (i=0;i<119;i++) { -+ printk(KERN_ERR " %3d: ",i); -+ if (GPDR(i) & GPIO_bit(i)) -+ printk("O "); -+ else -+ printk("I "); -+ printk("%d ",(GPLR(i) & GPIO_bit(i)) != 0); -+ printk("%d ",((GAFR(i) & (0x3 << (((i) & 0xf)*2)))) >> (((i) & 0xf)*2) ); -+ if (GEDR(i) & GPIO_bit(i)) -+ printk("ED "); -+ if (GRER(i) & GPIO_bit(i)) -+ printk("RE "); -+ if (GFER(i) & GPIO_bit(i)) -+ printk("FE "); -+ -+ printk("\n"); -+ } -+ return sprintf(buf, "EGPIO: %x\n",hx2750_egpio_current); -+} -+ -+static ssize_t clregpio_store(struct device *dev, const char *buf, size_t count) -+{ -+ unsigned int val = simple_strtoul(buf, NULL, 10); -+ -+ hx2750_clear_egpio(1 << val); -+ -+ return count; -+} -+ -+static DEVICE_ATTR(clregpio, 0644, clregpio_show, clregpio_store); -+ -+ -+static ssize_t gpioclr_store(struct device *dev, const char *buf, size_t count) -+{ -+ int prod; -+ prod = simple_strtoul(buf, NULL, 10); -+ -+ GPCR(prod) = GPIO_bit(prod); -+ -+ return count; -+} -+ -+static DEVICE_ATTR(gpioclr, 0644, gpio_show, gpioclr_store); -+ -+static ssize_t gpioset_store(struct device *dev, const char *buf, size_t count) -+{ -+ int prod; -+ prod = simple_strtoul(buf, NULL, 10); -+ -+ GPSR(prod) = GPIO_bit(prod); -+ -+ return count; -+} -+ -+static DEVICE_ATTR(gpioset, 0644, gpio_show, gpioset_store); -+ -+ -+static ssize_t ssp2read_store(struct device *dev, const char *buf, size_t count) -+{ -+ unsigned int val = simple_strtoul(buf, NULL, 16); -+ -+ printk("Read: %08x\n",hx2750_ssp2_read(val)); -+ -+ return count; -+} -+ -+static DEVICE_ATTR(ssp2read, 0200, NULL, ssp2read_store);
-+
-+static ssize_t ssp2write_store(struct device *dev, const char *buf, size_t count) -+{ -+ unsigned int val = simple_strtoul(buf, NULL, 16); -+ -+ printk("Write: %08x\n",hx2750_ssp2_write(val)); -+ -+ return count; -+} -+ -+static DEVICE_ATTR(ssp2write, 0200, NULL, ssp2write_store); -+ -+ -+static ssize_t sspr_store(struct device *dev, const char *buf, size_t count) -+{ -+ unsigned long val,ret; -+ val = simple_strtoul(buf, NULL, 0); -+ -+ hx2750_tsc2101_send(1<<15,val,&ret,1); -+ -+ printk("Response: %x\n",ret); -+ -+ return count; -+} -+ -+static DEVICE_ATTR(sspr, 0200, NULL, sspr_store); -+ -+static ssize_t sspw_store(struct device *dev, const char *buf, size_t count) -+{ -+ int val,ret; -+ sscanf(buf, "%lx %lx", &val, &ret); -+ -+ hx2750_tsc2101_send(0,val,&ret,1); -+ -+ return count; -+} -+ -+static DEVICE_ATTR(sspw, 0200, NULL, sspw_store); -+ -+ -+extern struct pm_ops pxa_pm_ops; -+extern void pxa_cpu_resume(void); -+extern unsigned long pxa_pm_pspr_value; -+ -+static int (*pxa_pm_enter_orig)(suspend_state_t state); -+ -+//static struct { -+// u32 ffier, fflcr, ffmcr, ffspr, ffisr, ffdll, ffdlh; -+//} sys_ctx; -+ -+u32 resstruct[20]; -+ -+static int hx2750_pxa_pm_enter(suspend_state_t state) -+{ -+ int i; -+ u32 save[10]; -+ -+ PWER = 0xC0000003;// | PWER_RTC; -+ PFER = 0x00000003; -+ PRER = 0x00000003; -+ -+ PGSR0=0x00000018; -+ PGSR1=0x00000380; -+ PGSR2=0x00800000; -+ PGSR3=0x00500400; -+ -+ //PVCR=0x494; or 0x14; -+ //PVCR=0x14; -+ //PCMD0=0x41f; -+ //i=PISR; -+ //PICR=0x00000062; -+ -+ //PCFR=0x457; -+ //PCFR=0x70; // Does not resume from sleep -+ PCFR=0x077; // was 0x477 -+ PSLR=0xff100004; -+ -+ resstruct[0]=0x0000b4e6; -+ resstruct[1]=0x00000001; -+ resstruct[2]=virt_to_phys(pxa_cpu_resume); -+ resstruct[3]=0xffffffff; //value for r0 -+ -+ resstruct[14]=0x00000078; //mcr 15, 0, r0, cr1, cr0, {0} -+ resstruct[15]=0x00000000; //mcr 15, 0, r0, cr1, cr1, {0} 0xffffffff
-+ resstruct[16]=0xa0000000; //mcr 15, 0, r0, cr2, cr0, {0} 0xa0748000
-+ resstruct[17]=0x00000001; //mcr 15, 0, r0, cr3, cr0, {0} 0x00000015
-+ resstruct[18]=0x00000000; //mcr 15, 0, r0, cr13, cr0, {0} 0x00000000
-+ -+ pxa_pm_pspr_value=virt_to_phys(&resstruct[0]); -+ -+ hx2750_send_egpio(3); -+ -+ pxa_gpio_mode(87 | GPIO_OUT | GPIO_DFLT_HIGH); -+ -+ //sys_ctx.ffier = FFIER; -+ //sys_ctx.fflcr = FFLCR; -+ //sys_ctx.ffmcr = FFMCR; -+ //sys_ctx.ffspr = FFSPR; -+ //sys_ctx.ffisr = FFISR; -+ //FFLCR |= 0x80; -+ //sys_ctx.ffdll = FFDLL; -+ //sys_ctx.ffdlh = FFDLH; -+ //FFLCR &= 0xef; -+ -+ pxa_pm_enter_orig(state); -+ -+ //FFMCR = sys_ctx.ffmcr; -+ //FFSPR = sys_ctx.ffspr; -+ //FFLCR = sys_ctx.fflcr; -+ //FFLCR |= 0x80; -+ //FFDLH = sys_ctx.ffdlh; -+ //FFDLL = sys_ctx.ffdll; -+ //FFLCR = sys_ctx.fflcr; -+ //FFISR = sys_ctx.ffisr; -+ //FFLCR = 0x07; -+ //FFIER = sys_ctx.ffier; -+ -+ return 0; -+} -+ -+static irqreturn_t hx2750_charge_int(int irq, void *dev_id, struct pt_regs *regs) -+{ -+ if ((GPLR(HX2750_GPIO_EXTPWR) & GPIO_bit(HX2750_GPIO_EXTPWR)) == 0) { -+ printk("Charging On\n"); -+ GPCR(HX2750_GPIO_CHARGE); -+ } else { -+ printk("Charging Off\n"); -+ GPSR(HX2750_GPIO_CHARGE); -+ } -+ -+ return IRQ_HANDLED; -+} -+ -+ -+ -+ -+static irqreturn_t hx2750_interrupt(int irq, void *dev_id, struct pt_regs *regs) -+{ -+ printk("Input %d changed.\n", irq-32); -+ -+ return IRQ_HANDLED; -+} -+ -+ -+static int __init hx2750_test_probe(struct device *dev) -+{ -+ pxa_gpio_mode(21 | GPIO_OUT | GPIO_DFLT_HIGH); -+ pxa_gpio_mode(HX2750_GPIO_CHARGE | GPIO_OUT | GPIO_DFLT_HIGH); -+ pxa_gpio_mode(83 | GPIO_OUT | GPIO_DFLT_HIGH); -+ pxa_gpio_mode(HX2750_GPIO_BIOPWR | GPIO_OUT | GPIO_DFLT_HIGH); -+ pxa_gpio_mode(116 | GPIO_OUT | GPIO_DFLT_HIGH); -+ pxa_gpio_mode(118 | GPIO_OUT | GPIO_DFLT_HIGH); -+ -+ -+ //pxa_gpio_mode(HX2750_GPIO_CF_PWR | GPIO_OUT | GPIO_DFLT_LOW); -+ pxa_gpio_mode(HX2750_GPIO_CF_PWR | GPIO_OUT | GPIO_DFLT_HIGH); -+ pxa_gpio_mode(HX2750_GPIO_BTPWR | GPIO_OUT | GPIO_DFLT_LOW); -+ pxa_gpio_mode(79 | GPIO_OUT | GPIO_DFLT_LOW); -+ pxa_gpio_mode(85 | GPIO_OUT | GPIO_DFLT_LOW); -+ pxa_gpio_mode(HX2750_GPIO_LEDMAIL | GPIO_OUT | GPIO_DFLT_LOW); -+ pxa_gpio_mode(107 | GPIO_OUT | GPIO_DFLT_LOW); -+ pxa_gpio_mode(114 | GPIO_OUT | GPIO_DFLT_LOW); -+ -+ pxa_gpio_mode(HX2750_GPIO_BATTCOVER1 | GPIO_IN); -+ pxa_gpio_mode(HX2750_GPIO_BATTCOVER2 | GPIO_IN); -+ pxa_gpio_mode(HX2750_GPIO_CF_IRQ | GPIO_IN); -+ pxa_gpio_mode(HX2750_GPIO_USBCONNECT | GPIO_IN); -+ pxa_gpio_mode(HX2750_GPIO_CF_DETECT | GPIO_IN); -+ pxa_gpio_mode(HX2750_GPIO_EXTPWR | GPIO_IN); -+ -+ pxa_gpio_mode(HX2750_GPIO_BATLVL | GPIO_IN); -+ //pxa_gpio_mode(HX2750_GPIO_CF_WIFIIRQ | GPIO_IN); -+ pxa_gpio_mode(80 | GPIO_IN); -+ pxa_gpio_mode(HX2750_GPIO_HP_JACK | GPIO_IN); -+ pxa_gpio_mode(115 | GPIO_IN); -+ pxa_gpio_mode(119 | GPIO_IN); -+ -+ request_irq(IRQ_GPIO(HX2750_GPIO_BATLVL), hx2750_interrupt, SA_INTERRUPT, "hx2750test", NULL); -+ //request_irq(IRQ_GPIO(HX2750_GPIO_CF_WIFIIRQ), hx2750_interrupt, SA_INTERRUPT, "hx2750test", NULL); -+ request_irq(IRQ_GPIO(80), hx2750_interrupt, SA_INTERRUPT, "hx2750test", NULL); -+ request_irq(IRQ_GPIO(115), hx2750_interrupt, SA_INTERRUPT, "hx2750test", NULL); -+ request_irq(IRQ_GPIO(119), hx2750_interrupt, SA_INTERRUPT, "hx2750test", NULL); -+ request_irq(IRQ_GPIO(HX2750_GPIO_SR_CLK2), hx2750_interrupt, SA_INTERRUPT, "hx2750test", NULL); -+ -+ //request_irq(IRQ_GPIO(10), hx2750_interrupt, SA_INTERRUPT, "hx2750test", NULL); -+ -+ set_irq_type(IRQ_GPIO(HX2750_GPIO_BATLVL),IRQT_BOTHEDGE); -+ //set_irq_type(IRQ_GPIO(HX2750_GPIO_CF_WIFIIRQ),IRQT_BOTHEDGE); -+ set_irq_type(IRQ_GPIO(80),IRQT_BOTHEDGE); -+ set_irq_type(IRQ_GPIO(115),IRQT_BOTHEDGE); -+ set_irq_type(IRQ_GPIO(119),IRQT_BOTHEDGE); -+ set_irq_type(IRQ_GPIO(HX2750_GPIO_SR_CLK2),IRQT_BOTHEDGE); -+ -+ //set_irq_type(IRQ_GPIO(10),IRQT_BOTHEDGE); -+ -+ printk("hx2750 Test Code Initialized.\n"); -+ device_create_file(dev, &dev_attr_test1); -+ device_create_file(dev, &dev_attr_test2); -+ device_create_file(dev, &dev_attr_setegpio); -+ device_create_file(dev, &dev_attr_clregpio); -+ device_create_file(dev, &dev_attr_gpioset); -+ device_create_file(dev, &dev_attr_gpioclr); -+ device_create_file(dev, &dev_attr_sspr); -+ device_create_file(dev, &dev_attr_sspw); -+ device_create_file(dev, &dev_attr_ssp2read); -+ device_create_file(dev, &dev_attr_ssp2write); -+ -+ request_irq(HX2750_IRQ_GPIO_EXTPWR, hx2750_charge_int, SA_INTERRUPT, "hx2750_extpwr", NULL); -+ set_irq_type(HX2750_IRQ_GPIO_EXTPWR, IRQT_BOTHEDGE); -+ -+ pxa_pm_enter_orig=pxa_pm_ops.enter; -+ pxa_pm_ops.enter=hx2750_pxa_pm_enter; -+ -+ return 0; -+} -+ -+static struct device_driver hx2750_test_driver = { -+ .name = "hx2750-test", -+ .bus = &platform_bus_type, -+ .probe = hx2750_test_probe, -+// .remove = hx2750_bl_remove, -+// .suspend = hx2750_bl_suspend, -+// .resume = hx2750_bl_resume, -+}; -+ -+ -+static int __init hx2750_test_init(void) -+{ -+ return driver_register(&hx2750_test_driver); -+} -+ -+ -+static void __exit hx2750_test_exit(void) -+{ -+ driver_unregister(&hx2750_test_driver); -+} -+ -+module_init(hx2750_test_init); -+module_exit(hx2750_test_exit); -+ -+MODULE_AUTHOR("Richard Purdie <richard@o-hand.com>"); -+MODULE_DESCRIPTION("iPAQ hx2750 Backlight Driver"); -+MODULE_LICENSE("GPLv2"); ---- linux-2.6.24-rc1.orig/arch/arm/mach-pxa/Kconfig -+++ linux-2.6.24-rc1/arch/arm/mach-pxa/Kconfig -@@ -83,6 +83,15 @@ - bool "Sharp PXA270 models (SL-Cxx00)" - select PXA27x - -+config MACH_HX2750 -+ bool "HP iPAQ hx2750" -+ select PXA27x -+ select PXA_KEYS -+ select MFD_TSC2101 -+ select PXA_SSP -+ help -+ This enables support for the HP iPAQ HX2750 handheld. -+ - endchoice - - endif -@@ -181,3 +190,4 @@ - help - Enable support for PXA2xx SSP ports - endif -+ ---- /dev/null -+++ linux-2.6.24-rc1/arch/arm/mach-pxa/hx2750.c -@@ -0,0 +1,450 @@ -+/* -+ * Machine definitions for HP iPAQ hx2750 -+ * -+ * Copyright 2005 Openedhand Ltd. -+ * -+ * Author: Richard Purdie <richard@o-hand.com> -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ * -+ */ -+ -+ -+#include <linux/init.h> -+#include <linux/kernel.h> -+#include <linux/ioport.h> -+#include <linux/platform_device.h> -+#include <linux/delay.h> -+#include <linux/input.h> -+#include <linux/irq.h> -+#include <linux/mmc/host.h> -+#include <linux/mfd/tsc2101.h> -+ -+#include <asm/mach-types.h> -+#include <asm/hardware.h> -+#include <asm/mach/arch.h> -+ -+#include <asm/arch/hx2750.h> -+#include <asm/arch/pxa-regs.h> -+#include <asm/arch/pxa_keys.h> -+#include <asm/mach/map.h> -+#include <asm/arch/udc.h> -+#include <asm/arch/mmc.h> -+#include <asm/arch/audio.h> -+#include <asm/arch/pxafb.h> -+#include <asm/arch/ssp.h> -+ -+#include "generic.h" -+ -+ -+/* -+ * Keys Support -+ */ -+static struct pxa_keys_button hx2750_button_table[] = { -+ { KEY_POWER, HX2750_GPIO_KEYPWR, PXAKEY_PWR_KEY }, -+ { KEY_LEFT, HX2750_GPIO_KEYLEFT, 0 }, -+ { KEY_RIGHT, HX2750_GPIO_KEYRIGHT, 0 }, -+ { KEY_KP0, HX2750_GPIO_KEYCAL, 0 }, -+ { KEY_KP1, HX2750_GPIO_KEYTASK, 0 }, -+ { KEY_KP2, HX2750_GPIO_KEYSIDE, 0 }, -+ { KEY_ENTER, HX2750_GPIO_KEYENTER, 0 }, -+ { KEY_KP3, HX2750_GPIO_KEYCON, 0 }, //KEY_CONTACTS -+ { KEY_MAIL, HX2750_GPIO_KEYMAIL, 0 }, -+ { KEY_UP, HX2750_GPIO_KEYUP, 0 }, -+ { KEY_DOWN, HX2750_GPIO_KEYDOWN, 0 }, -+}; -+ -+static struct pxa_keys_platform_data hx2750_pxa_keys_data = { -+ .buttons = hx2750_button_table, -+ .nbuttons = ARRAY_SIZE(hx2750_button_table), -+}; -+ -+static struct platform_device hx2750_pxa_keys = { -+ .name = "pxa2xx-keys", -+ .dev = { -+ .platform_data = &hx2750_pxa_keys_data, -+ }, -+}; -+ -+ -+/* -+ * Backlight Device -+ */ -+extern struct platform_device pxafb_device; -+ -+static struct platform_device hx2750_bl_device = { -+ .name = "hx2750-bl", -+ .id = -1, -+// .dev = { -+// .parent = &pxafb_device.dev, -+// } -+}; -+ -+ -+/* -+ * UDC/USB -+ */ -+static int hx2750_udc_is_connected (void) -+{ -+ return GPLR0 & GPIO_bit(HX2750_GPIO_USBCONNECT); -+} -+ -+//static void hx2750_udc_command (int cmd) -+//{ -+//} -+ -+static struct pxa2xx_udc_mach_info hx2750_udc_mach_info = { -+ .udc_is_connected = hx2750_udc_is_connected, -+// .udc_command = hx2750_udc_command, -+}; -+ -+ -+/* -+ * SSP Devices Setup -+ */ -+static struct ssp_dev hx2750_ssp_dev1; -+static struct ssp_dev hx2750_ssp_dev2; -+static struct ssp_dev hx2750_ssp_dev3; -+ -+void hx2750_ssp_init(void) -+{ -+ pxa_gpio_mode(HX2750_GPIO_TSC2101_SS | GPIO_OUT | GPIO_DFLT_HIGH); -+ -+ if (ssp_init(&hx2750_ssp_dev1, 1, 0)) -+ printk(KERN_ERR "Unable to register SSP1 handler!\n"); -+ else { -+ ssp_disable(&hx2750_ssp_dev1); -+ ssp_config(&hx2750_ssp_dev1, (SSCR0_Motorola | (SSCR0_DSS & 0x0f )), SSCR1_SPH, 0, SSCR0_SerClkDiv(6)); -+ ssp_enable(&hx2750_ssp_dev1); -+ hx2750_set_egpio(HX2750_EGPIO_TSC_PWR); -+ } -+ -+// if (ssp_init(&hx2750_ssp_dev2, 2, 0)) -+// printk(KERN_ERR "Unable to register SSP2 handler!\n"); -+// else { -+// ssp_disable(&hx2750_ssp_dev2); -+// ssp_config(&hx2750_ssp_dev2, (SSCR0_TI | (SSCR0_DSS & 0x09 )), 0, 0, SSCR0_SerClkDiv(140)); -+// ssp_enable(&hx2750_ssp_dev2); -+// } -+// -+ if (ssp_init(&hx2750_ssp_dev3, 3, 0)) -+ printk(KERN_ERR "Unable to register SSP3 handler!\n"); -+ else { -+ ssp_disable(&hx2750_ssp_dev3); -+ ssp_config(&hx2750_ssp_dev3, (SSCR0_Motorola | (SSCR0_DSS & 0x07 )), SSCR1_SPO | SSCR1_SPH, 0, SSCR0_SerClkDiv(166)); -+ ssp_enable(&hx2750_ssp_dev3); -+ } -+ -+ printk("SSP Devices Initialised\n"); -+ -+ return; -+} -+ -+struct ssp_state ssp1; -+ -+void hx2750_ssp_suspend(void) -+{ -+ ssp_disable(&hx2750_ssp_dev1); -+ ssp_save_state(&hx2750_ssp_dev1,&ssp1); -+ hx2750_clear_egpio(HX2750_EGPIO_TSC_PWR); -+} -+ -+void hx2750_ssp_resume(void) -+{ -+ hx2750_set_egpio(HX2750_EGPIO_TSC_PWR); -+ ssp_restore_state(&hx2750_ssp_dev1,&ssp1); -+ ssp_enable(&hx2750_ssp_dev1); -+} -+ -+void hx2750_ssp_init2(void) -+{ -+ printk("Stage 1: %x\n",CKEN); -+ if (ssp_init(&hx2750_ssp_dev2, 2, 0)) -+ printk(KERN_ERR "Unable to register SSP2 handler!\n"); -+ else { -+ printk("Stage 2: %x\n",CKEN); -+ ssp_disable(&hx2750_ssp_dev2); -+ printk("Stage 3: %x\n",CKEN); -+ ssp_config(&hx2750_ssp_dev2, (SSCR0_TI | (SSCR0_DSS & 0x09 )) | SSCR0_SSE, 0, 0, SSCR0_SerClkDiv(212)); -+ printk("Stage 4: %x\n",CKEN); -+ ssp_enable(&hx2750_ssp_dev2); -+ printk("Stage 5: %x\n",CKEN); -+ } -+ printk("SSP Device2 Initialised\n"); -+ -+ printk("Sent: 0x3ff\n"); -+ ssp_write_word(&hx2750_ssp_dev2,0x3ff); -+ -+ return; -+} -+ -+void hx2750_ssp2_reset(void) -+{ -+ ssp_write_word(&hx2750_ssp_dev2,0x000); -+ ssp_write_word(&hx2750_ssp_dev2,0x000); -+ -+} -+ -+unsigned long hx2750_ssp2_read(void) -+{ -+ u32 ret = 0; -+ ssp_read_word(&hx2750_ssp_dev2, &ret); -+ return ret; -+} -+ -+void hx2750_ssp2_write(unsigned long data) -+{ -+ ssp_write_word(&hx2750_ssp_dev2, data); -+} -+ -+ -+/* -+ * Extra hx2750 Specific GPIOs -+ */ -+void hx2750_send_egpio(unsigned int val) -+{ -+ int i; -+ -+ GPCR0 = GPIO_bit(HX2750_GPIO_SR_STROBE); -+ GPCR1 = GPIO_bit(HX2750_GPIO_SR_CLK1); -+ -+ for (i=0;i<12;i++) { -+ if (val & 0x01) -+ GPSR2 = GPIO_bit(HX2750_GPIO_GPIO_DIN); -+ else -+ GPCR2 = GPIO_bit(HX2750_GPIO_GPIO_DIN); -+ val >>= 1; -+ GPSR1 = GPIO_bit(HX2750_GPIO_SR_CLK1); -+ GPCR1 = GPIO_bit(HX2750_GPIO_SR_CLK1); -+ } -+ -+ GPSR0 = GPIO_bit(HX2750_GPIO_SR_STROBE); -+ GPCR0 = GPIO_bit(HX2750_GPIO_SR_STROBE); -+} -+ -+EXPORT_SYMBOL(hx2750_send_egpio); -+ -+unsigned int hx2750_egpio_current; -+ -+void hx2750_set_egpio(unsigned int gpio) -+{ -+ hx2750_egpio_current|=gpio; -+ -+ hx2750_send_egpio(hx2750_egpio_current); -+} -+EXPORT_SYMBOL(hx2750_set_egpio); -+ -+void hx2750_clear_egpio(unsigned int gpio) -+{ -+ hx2750_egpio_current&=~gpio; -+ -+ hx2750_send_egpio(hx2750_egpio_current); -+} -+EXPORT_SYMBOL(hx2750_clear_egpio); -+ -+ -+/* -+ * Touchscreen/Sound/Battery Status -+ */ -+void hx2750_tsc2101_send(int read, int command, int *values, int numval) -+{ -+ u32 ret = 0; -+ int i; -+ -+ GPCR0 = GPIO_bit(HX2750_GPIO_TSC2101_SS); -+ -+ ssp_write_word(&hx2750_ssp_dev1, command | read); -+ /* dummy read */ -+ ssp_read_word(&hx2750_ssp_dev1, &ret); -+ -+ for (i=0; i < numval; i++) { -+ if (read) { -+ ssp_write_word(&hx2750_ssp_dev1, 0); -+ ssp_read_word(&hx2750_ssp_dev1, &values[i]); -+ } else { -+ ssp_write_word(&hx2750_ssp_dev1, values[i]); -+ ssp_read_word(&hx2750_ssp_dev1, &ret); -+ } -+ } -+ -+ GPSR0 = GPIO_bit(HX2750_GPIO_TSC2101_SS); -+} -+ -+static int hx2750_tsc2101_pendown(void) -+{ -+ if ((GPLR(HX2750_GPIO_PENDOWN) & GPIO_bit(HX2750_GPIO_PENDOWN)) == 0) -+ return 1; -+ return 0; -+} -+ -+static struct tsc2101_platform_info hx2750_tsc2101_info = { -+ .send = hx2750_tsc2101_send, -+ .suspend = hx2750_ssp_suspend, -+ .resume = hx2750_ssp_resume, -+ .irq = HX2750_IRQ_GPIO_PENDOWN, -+ .pendown = hx2750_tsc2101_pendown, -+}; -+ -+struct platform_device tsc2101_device = { -+ .name = "tsc2101", -+ .dev = { -+ .platform_data = &hx2750_tsc2101_info, -+ //.parent = &corgissp_device.dev, -+ }, -+ .id = -1, -+}; -+ -+ -+/* -+ * MMC/SD Device -+ * -+ * The card detect interrupt isn't debounced so we delay it by 250ms -+ * to give the card a chance to fully insert/eject. -+ */ -+static struct pxamci_platform_data hx2750_mci_platform_data; -+ -+static int hx2750_mci_init(struct device *dev, irq_handler_t hx2750_detect_int, void *data) -+{ -+ int err; -+ -+ /* -+ * setup GPIO for PXA27x MMC controller -+ */ -+ pxa_gpio_mode(GPIO32_MMCCLK_MD); -+ pxa_gpio_mode(GPIO112_MMCCMD_MD); -+ pxa_gpio_mode(GPIO92_MMCDAT0_MD); -+ pxa_gpio_mode(GPIO109_MMCDAT1_MD); -+ pxa_gpio_mode(GPIO110_MMCDAT2_MD); -+ pxa_gpio_mode(GPIO111_MMCDAT3_MD); -+ pxa_gpio_mode(HX2750_GPIO_SD_DETECT | GPIO_IN); -+ pxa_gpio_mode(HX2750_GPIO_SD_READONLY | GPIO_IN); -+ -+ hx2750_mci_platform_data.detect_delay = msecs_to_jiffies(250); -+ -+ err = request_irq(HX2750_IRQ_GPIO_SD_DETECT, hx2750_detect_int, -+ IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, -+ "MMC card detect", data); -+ if (err) { -+ printk(KERN_ERR "hx2750_mci_init: MMC/SD: can't request MMC card detect IRQ\n"); -+ return -1; -+ } -+ -+ return 0; -+} -+ -+static void hx2750_mci_setpower(struct device *dev, unsigned int vdd) -+{ -+ struct pxamci_platform_data* p_d = dev->platform_data; -+ -+ if (( 1 << vdd) & p_d->ocr_mask) -+ hx2750_set_egpio(HX2750_EGPIO_SD_PWR); -+ else -+ hx2750_clear_egpio(HX2750_EGPIO_SD_PWR); -+} -+ -+static void hx2750_mci_exit(struct device *dev, void *data) -+{ -+ free_irq(HX2750_IRQ_GPIO_SD_DETECT, data); -+} -+ -+static struct pxamci_platform_data hx2750_mci_platform_data = { -+ .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, -+ .init = hx2750_mci_init, -+ .setpower = hx2750_mci_setpower, -+ .exit = hx2750_mci_exit, -+}; -+ -+ -+/* -+ * FrameBuffer -+ */ -+static struct pxafb_mode_info hx2750_pxafb_modes = { -+ .pixclock = 288462, -+ .xres = 240, -+ .yres = 320, -+ .bpp = 16, -+ .hsync_len = 20, -+ .left_margin = 42, -+ .right_margin = 18, -+ .vsync_len = 4, -+ .upper_margin = 3, -+ .lower_margin = 4, -+ .sync = 0, -+}; -+ -+static struct pxafb_mach_info hx2750_pxafb_info = { -+ .modes = &hx2750_pxafb_modes, -+ .num_modes = 1, -+ .fixed_modes = 1, -+ .lccr0 = LCCR0_Color | LCCR0_Sngl | LCCR0_Act, -+ .lccr3 = LCCR3_PixFlEdg | LCCR3_OutEnH, -+ .pxafb_backlight_power = NULL, -+}; -+ -+ -+/* -+ * Test Device -+ */ -+static struct platform_device hx2750_test_device = { -+ .name = "hx2750-test", -+ .id = -1, -+}; -+ -+ -+/* Initialization code */ -+static struct platform_device *devices[] __initdata = { -+ &hx2750_bl_device, -+ &hx2750_test_device, -+ &hx2750_pxa_keys, -+ &tsc2101_device, -+}; -+ -+static void __init hx2750_init( void ) -+{ -+ PWER = 0xC0000003;// | PWER_RTC; -+ PFER = 0x00000003; -+ PRER = 0x00000003; -+ -+ PGSR0=0x00000018; -+ PGSR1=0x00000380; -+ PGSR2=0x00800000; -+ PGSR3=0x00500400; -+ -+ //PCFR |= PCFR_OPDE; -+ PCFR=0x77; -+ PSLR=0xff100000; -+ //PCFR=0x10; - does not return from suspend -+ -+ //PCFR= 0x00004040; -+ //PSLR= 0xff400f04; -+ -+ /* Setup Extra GPIO Bank access */ -+ pxa_gpio_mode(HX2750_GPIO_GPIO_DIN | GPIO_OUT | GPIO_DFLT_HIGH); -+ pxa_gpio_mode(HX2750_GPIO_SR_CLK1 | GPIO_OUT | GPIO_DFLT_LOW); -+ pxa_gpio_mode(HX2750_GPIO_SR_CLK2 | GPIO_IN); -+ pxa_gpio_mode(HX2750_GPIO_SR_STROBE | GPIO_OUT | GPIO_DFLT_LOW); -+ -+ /* Init Extra GPIOs - Bootloader reset default is 0x484 */ -+ /* This is 0xe84 */ -+ hx2750_set_egpio(HX2750_EGPIO_2 | HX2750_EGPIO_7 | HX2750_EGPIO_LCD_PWR | HX2750_EGPIO_BL_PWR | HX2750_EGPIO_WIFI_PWR); -+ -+ pxa_set_udc_info(&hx2750_udc_mach_info); -+ pxa_set_mci_info(&hx2750_mci_platform_data); -+ set_pxa_fb_info(&hx2750_pxafb_info); -+ hx2750_ssp_init(); -+ platform_add_devices (devices, ARRAY_SIZE (devices)); -+} -+ -+ -+MACHINE_START(HX2750, "HP iPAQ HX2750") -+ .phys_io = 0x40000000, -+ .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc, -+ .boot_params = 0xa0000100, -+ .map_io = pxa_map_io, -+ .init_irq = pxa_init_irq, -+ .timer = &pxa_timer, -+ .init_machine = hx2750_init, -+MACHINE_END -+ ---- linux-2.6.24-rc1.orig/arch/arm/mach-pxa/pm.c -+++ linux-2.6.24-rc1/arch/arm/mach-pxa/pm.c -@@ -17,6 +17,7 @@ - #include <linux/time.h> - - #include <asm/hardware.h> -+#include <asm/mach-types.h> - #include <asm/memory.h> - #include <asm/system.h> - #include <asm/arch/pm.h> -@@ -91,6 +92,9 @@ - .enter = pxa_pm_enter, - }; - -+unsigned long pxa_pm_pspr_value; -+extern void pxa_cpu_resume(void); -+ - static int __init pxa_pm_init(void) - { - if (!pxa_cpu_pm_fns) { -@@ -104,6 +108,7 @@ - return -ENOMEM; - } - -+ pxa_pm_pspr_value=virt_to_phys(pxa_cpu_resume); - suspend_set_ops(&pxa_pm_ops); - return 0; - } ---- linux-2.6.24-rc1.orig/arch/arm/mach-pxa/pxa27x.c -+++ linux-2.6.24-rc1/arch/arm/mach-pxa/pxa27x.c -@@ -259,6 +259,8 @@ - RESTORE(PSTR); - } - -+extern unsigned long pxa_pm_pspr_value; -+ - void pxa27x_cpu_pm_enter(suspend_state_t state) - { - extern void pxa_cpu_standby(void); -@@ -281,7 +283,7 @@ - break; - case PM_SUSPEND_MEM: - /* set resume return address */ -- PSPR = virt_to_phys(pxa_cpu_resume); -+ PSPR = pxa_pm_pspr_value; - pxa27x_cpu_suspend(PWRMODE_SLEEP); - break; - } ---- linux-2.6.24-rc1.orig/arch/arm/mach-pxa/pxa25x.c -+++ linux-2.6.24-rc1/arch/arm/mach-pxa/pxa25x.c -@@ -200,6 +200,8 @@ - RESTORE(PSTR); - } - -+extern unsigned long pxa_pm_pspr_value; -+ - static void pxa25x_cpu_pm_enter(suspend_state_t state) - { - CKEN = 0; -@@ -207,7 +209,7 @@ - switch (state) { - case PM_SUSPEND_MEM: - /* set resume return address */ -- PSPR = virt_to_phys(pxa_cpu_resume); -+ PSPR = pxa_pm_pspr_value; - pxa25x_cpu_suspend(PWRMODE_SLEEP); - break; - } diff --git a/meta/packages/linux/linux-rp-2.6.23+2.6.24-rc8/mmcsd_no_scr_check-r2.patch b/meta/packages/linux/linux-rp-2.6.23+2.6.24-rc8/mmcsd_no_scr_check-r2.patch deleted file mode 100644 index ac2245f088..0000000000 --- a/meta/packages/linux/linux-rp-2.6.23+2.6.24-rc8/mmcsd_no_scr_check-r2.patch +++ /dev/null @@ -1,29 +0,0 @@ ---- - drivers/mmc/core/sd.c | 11 ++++++----- - 1 file changed, 6 insertions(+), 5 deletions(-) - -Index: linux-2.6.23/drivers/mmc/core/sd.c -=================================================================== ---- linux-2.6.23.orig/drivers/mmc/core/sd.c 2007-10-17 11:33:26.000000000 +0200 -+++ linux-2.6.23/drivers/mmc/core/sd.c 2007-10-17 11:33:49.000000000 +0200 -@@ -173,14 +173,15 @@ - - scr_struct = UNSTUFF_BITS(resp, 60, 4); - if (scr_struct != 0) { -- printk(KERN_ERR "%s: unrecognised SCR structure version %d\n", -+ printk(KERN_WARNING "%s: unrecognised SCR structure version %d\n", - mmc_hostname(card->host), scr_struct); -- return -EINVAL; -+ scr->sda_vsn = 0; -+ scr->bus_widths = 0; -+ } else { -+ scr->sda_vsn = UNSTUFF_BITS(resp, 56, 4); -+ scr->bus_widths = UNSTUFF_BITS(resp, 48, 4); - } - -- scr->sda_vsn = UNSTUFF_BITS(resp, 56, 4); -- scr->bus_widths = UNSTUFF_BITS(resp, 48, 4); -- - return 0; - } - diff --git a/meta/packages/linux/linux-rp-2.6.23+2.6.24-rc8/pda-power.patch b/meta/packages/linux/linux-rp-2.6.23+2.6.24-rc8/pda-power.patch deleted file mode 100644 index face2f4ef2..0000000000 --- a/meta/packages/linux/linux-rp-2.6.23+2.6.24-rc8/pda-power.patch +++ /dev/null @@ -1,3373 +0,0 @@ ---- - arch/arm/Kconfig | 2 - drivers/Kconfig | 2 - drivers/Makefile | 1 - drivers/power/Kconfig | 70 +++++ - drivers/power/Makefile | 28 ++ - drivers/power/adc_battery.c | 278 +++++++++++++++++++++ - drivers/power/apm_power.c | 247 +++++++++++++++++++ - drivers/power/ds2760_battery.c | 475 +++++++++++++++++++++++++++++++++++++ - drivers/power/micro_battery.c | 257 ++++++++++++++++++++ - drivers/power/olpc_battery.c | 302 +++++++++++++++++++++++ - drivers/power/pda_power.c | 263 ++++++++++++++++++++ - drivers/power/pmu_battery.c | 215 ++++++++++++++++ - drivers/power/power_supply.h | 42 +++ - drivers/power/power_supply_core.c | 168 +++++++++++++ - drivers/power/power_supply_leds.c | 188 ++++++++++++++ - drivers/power/power_supply_sysfs.c | 289 ++++++++++++++++++++++ - drivers/power/simpad-battery.c | 242 ++++++++++++++++++ - include/linux/power_supply.h | 175 +++++++++++++ - 18 files changed, 3244 insertions(+) - -Index: linux-2.6.22/drivers/power/adc_battery.c -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/drivers/power/adc_battery.c 2007-08-23 12:26:28.000000000 +0200 -@@ -0,0 +1,278 @@ -+/* -+ * Copyright (c) 2007 Paul Sokolovsky -+ * -+ * 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 -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ */ -+ -+//#define DEBUG -+ -+#include <linux/module.h> -+#include <linux/interrupt.h> -+#include <linux/pm.h> -+#include <linux/delay.h> -+#include <linux/workqueue.h> -+#include <linux/platform_device.h> -+#include <linux/power_supply.h> -+#include <linux/adc.h> -+#include <linux/adc_battery.h> -+ -+#include <asm/irq.h> -+ -+#define PIN_NO_VOLT 0 -+#define PIN_NO_CURR 1 -+#define PIN_NO_TEMP 2 -+ -+struct battery_adc_priv { -+ struct power_supply batt_cdev; -+ -+ struct battery_adc_platform_data *pdata; -+ -+ struct adc_request req; -+ struct adc_sense pins[3]; -+ struct adc_sense last_good_pins[3]; -+ -+ struct workqueue_struct *wq; -+ struct delayed_work work; -+}; -+ -+/* -+ * Battery properties -+ */ -+ -+static int adc_battery_get_property(struct power_supply *psy, -+ enum power_supply_property psp, -+ union power_supply_propval *val) -+{ -+ struct battery_adc_priv* drvdata = (struct battery_adc_priv*)psy; -+ int voltage; -+ -+ switch (psp) { -+ case POWER_SUPPLY_PROP_STATUS: -+ val->intval = drvdata->pdata->charge_status; -+ break; -+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: -+ val->intval = drvdata->pdata->battery_info.voltage_max_design; -+ break; -+ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: -+ val->intval = drvdata->pdata->battery_info.voltage_min_design; -+ break; -+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: -+ val->intval = drvdata->pdata->battery_info.charge_full_design; -+ break; -+ case POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN: -+ val->intval = drvdata->pdata->battery_info.charge_empty_design; -+ break; -+ case POWER_SUPPLY_PROP_VOLTAGE_NOW: -+ val->intval = drvdata->last_good_pins[PIN_NO_VOLT].value * drvdata->pdata->voltage_mult; -+ break; -+ case POWER_SUPPLY_PROP_CURRENT_NOW: -+ val->intval = drvdata->last_good_pins[PIN_NO_CURR].value * drvdata->pdata->current_mult; -+ break; -+ case POWER_SUPPLY_PROP_CHARGE_NOW: -+ /* We do calculations in mX, not uX, because todo it in uX we should use "long long"s, -+ * which is a mess (need to use do_div) when you need divide operation). */ -+ voltage = drvdata->last_good_pins[PIN_NO_VOLT].value * drvdata->pdata->voltage_mult; -+ val->intval = ((voltage/1000 - drvdata->pdata->battery_info.voltage_min_design/1000) * -+ (drvdata->pdata->battery_info.charge_full_design/1000 - -+ drvdata->pdata->battery_info.charge_empty_design/1000)) / -+ (drvdata->pdata->battery_info.voltage_max_design/1000 - -+ drvdata->pdata->battery_info.voltage_min_design/1000); -+ val->intval *= 1000; /* convert final result to uX */ -+ break; -+ case POWER_SUPPLY_PROP_TEMP: -+ val->intval = drvdata->last_good_pins[PIN_NO_TEMP].value * drvdata->pdata->temperature_mult / 1000; -+ break; -+ default: -+ return -EINVAL; -+ }; -+ return 0; -+} -+ -+/* -+ * Driver body -+ */ -+ -+static void adc_battery_query(struct battery_adc_priv *drvdata) -+{ -+ struct battery_adc_platform_data *pdata = drvdata->pdata; -+ int powered, charging; -+ -+ adc_request_sample(&drvdata->req); -+ -+ powered = power_supply_am_i_supplied(&drvdata->batt_cdev); -+ charging = pdata->is_charging ? pdata->is_charging() : -1; -+ -+ if (powered && charging) -+ pdata->charge_status = POWER_SUPPLY_STATUS_CHARGING; -+ else if (powered && !charging && charging != -1) -+ pdata->charge_status = POWER_SUPPLY_STATUS_FULL; -+ else -+ pdata->charge_status = POWER_SUPPLY_STATUS_DISCHARGING; -+ -+ /* Throw away invalid samples, this may happen soon after resume for example. */ -+ if (drvdata->pins[PIN_NO_VOLT].value > 0) { -+ memcpy(drvdata->last_good_pins, drvdata->pins, sizeof(drvdata->pins)); -+#ifdef DEBUG -+ printk("%d %d %d\n", drvdata->pins[PIN_NO_VOLT].value, -+ drvdata->pins[PIN_NO_CURR].value, -+ drvdata->pins[PIN_NO_TEMP].value); -+#endif -+ } -+} -+ -+static void adc_battery_charge_power_changed(struct power_supply *bat) -+{ -+ struct battery_adc_priv *drvdata = (struct battery_adc_priv*)bat; -+ cancel_delayed_work(&drvdata->work); -+ queue_delayed_work(drvdata->wq, &drvdata->work, 0); -+} -+ -+static void adc_battery_work_func(struct work_struct *work) -+{ -+ struct delayed_work *delayed_work = container_of(work, struct delayed_work, work); -+ struct battery_adc_priv *drvdata = container_of(delayed_work, struct battery_adc_priv, work); -+ -+ adc_battery_query(drvdata); -+ power_supply_changed(&drvdata->batt_cdev); -+ -+ queue_delayed_work(drvdata->wq, &drvdata->work, (5000 * HZ) / 1000); -+} -+ -+static int adc_battery_probe(struct platform_device *pdev) -+{ -+ int retval; -+ struct battery_adc_platform_data *pdata = pdev->dev.platform_data; -+ struct battery_adc_priv *drvdata; -+ int i, j; -+ enum power_supply_property props[] = { -+ POWER_SUPPLY_PROP_STATUS, -+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, -+ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, -+ POWER_SUPPLY_PROP_VOLTAGE_NOW, -+ POWER_SUPPLY_PROP_CURRENT_NOW, -+ POWER_SUPPLY_PROP_CHARGE_NOW, -+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, -+ POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN, -+ POWER_SUPPLY_PROP_TEMP, -+ }; -+ -+ // Initialize ts data structure. -+ drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL); -+ if (!drvdata) -+ return -ENOMEM; -+ -+ drvdata->batt_cdev.name = pdata->battery_info.name; -+ drvdata->batt_cdev.use_for_apm = pdata->battery_info.use_for_apm; -+ drvdata->batt_cdev.num_properties = ARRAY_SIZE(props); -+ drvdata->batt_cdev.get_property = adc_battery_get_property; -+ drvdata->batt_cdev.external_power_changed = -+ adc_battery_charge_power_changed; -+ -+ if (!pdata->voltage_pin) { -+ drvdata->batt_cdev.num_properties--; -+ props[3] = -1; -+ } -+ if (!pdata->current_pin) { -+ drvdata->batt_cdev.num_properties--; -+ props[4] = -1; -+ } -+ if (!pdata->temperature_pin) { -+ drvdata->batt_cdev.num_properties--; -+ props[8] = -1; -+ } -+ -+ drvdata->batt_cdev.properties = kmalloc( -+ sizeof(*drvdata->batt_cdev.properties) * -+ drvdata->batt_cdev.num_properties, GFP_KERNEL); -+ if (!drvdata->batt_cdev.properties) -+ return -ENOMEM; -+ -+ j = 0; -+ for (i = 0; i < ARRAY_SIZE(props); i++) { -+ if (props[i] == -1) -+ continue; -+ drvdata->batt_cdev.properties[j++] = props[i]; -+ } -+ -+ retval = power_supply_register(&pdev->dev, &drvdata->batt_cdev); -+ if (retval) { -+ printk("adc-battery: Error registering battery classdev"); -+ return retval; -+ } -+ -+ drvdata->req.senses = drvdata->pins; -+ drvdata->req.num_senses = ARRAY_SIZE(drvdata->pins); -+ drvdata->pins[PIN_NO_VOLT].name = pdata->voltage_pin; -+ drvdata->pins[PIN_NO_CURR].name = pdata->current_pin; -+ drvdata->pins[PIN_NO_TEMP].name = pdata->temperature_pin; -+ -+ adc_request_register(&drvdata->req); -+ -+ /* Here we assume raw values in mV */ -+ if (!pdata->voltage_mult) -+ pdata->voltage_mult = 1000; -+ /* Here we assume raw values in mA */ -+ if (!pdata->current_mult) -+ pdata->current_mult = 1000; -+ /* Here we assume raw values in 1/10 C */ -+ if (!pdata->temperature_mult) -+ pdata->temperature_mult = 1000; -+ -+ drvdata->pdata = pdata; -+ pdata->drvdata = drvdata; /* Seems ugly, we need better solution */ -+ -+ platform_set_drvdata(pdev, drvdata); -+ -+ // Load initial values ASAP -+ adc_battery_query(drvdata); -+ -+ // Still schedule next sampling soon -+ INIT_DELAYED_WORK(&drvdata->work, adc_battery_work_func); -+ drvdata->wq = create_workqueue(pdev->dev.bus_id); -+ if (!drvdata->wq) -+ return -ESRCH; -+ -+ queue_delayed_work(drvdata->wq, &drvdata->work, (5000 * HZ) / 1000); -+ -+ return retval; -+} -+ -+static int adc_battery_remove(struct platform_device *pdev) -+{ -+ struct battery_adc_priv *drvdata = platform_get_drvdata(pdev); -+ cancel_delayed_work(&drvdata->work); -+ destroy_workqueue(drvdata->wq); -+ power_supply_unregister(&drvdata->batt_cdev); -+ adc_request_unregister(&drvdata->req); -+ kfree(drvdata->batt_cdev.properties); -+ return 0; -+} -+ -+static struct platform_driver adc_battery_driver = { -+ .driver = { -+ .name = "adc-battery", -+ }, -+ .probe = adc_battery_probe, -+ .remove = adc_battery_remove, -+}; -+ -+static int __init adc_battery_init(void) -+{ -+ return platform_driver_register(&adc_battery_driver); -+} -+ -+static void __exit adc_battery_exit(void) -+{ -+ platform_driver_unregister(&adc_battery_driver); -+} -+ -+module_init(adc_battery_init) -+module_exit(adc_battery_exit) -+ -+MODULE_AUTHOR("Paul Sokolovsky"); -+MODULE_DESCRIPTION("Battery driver for ADC device"); -+MODULE_LICENSE("GPL"); -Index: linux-2.6.22/drivers/power/apm_power.c -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/drivers/power/apm_power.c 2007-08-23 12:13:52.000000000 +0200 -@@ -0,0 +1,247 @@ -+/* -+ * Copyright (c) 2007 Anton Vorontsov <cbou@mail.ru> -+ * Copyright (c) 2007 Eugeny Boger <eugenyboger@dgap.mipt.ru> -+ * -+ * Author: Eugeny Boger <eugenyboger@dgap.mipt.ru> -+ * -+ * Use consistent with the GNU GPL is permitted, -+ * provided that this copyright notice is -+ * preserved in its entirety in all copies and derived works. -+ */ -+ -+#include <linux/module.h> -+#include <linux/power_supply.h> -+#include <linux/apm-emulation.h> -+ -+#define PSY_PROP(psy, prop, val) psy->get_property(psy, \ -+ POWER_SUPPLY_PROP_##prop, val) -+ -+#define _MPSY_PROP(prop, val) main_battery->get_property(main_battery, \ -+ prop, val) -+ -+#define MPSY_PROP(prop, val) _MPSY_PROP(POWER_SUPPLY_PROP_##prop, val) -+ -+static struct power_supply *main_battery; -+ -+static void find_main_battery(void) -+{ -+ struct device *dev; -+ struct power_supply *bat, *batm; -+ union power_supply_propval full; -+ int max_charge = 0; -+ -+ main_battery = NULL; -+ batm = NULL; -+ list_for_each_entry(dev, &power_supply_class->devices, node) { -+ bat = dev_get_drvdata(dev); -+ /* If none of battery devices cantains 'use_for_apm' flag, -+ choice one with maximum design charge */ -+ if (!PSY_PROP(bat, CHARGE_FULL_DESIGN, &full)) { -+ if (full.intval > max_charge) { -+ batm = bat; -+ max_charge = full.intval; -+ } -+ } -+ -+ if (bat->use_for_apm) -+ main_battery = bat; -+ } -+ if (!main_battery) -+ main_battery = batm; -+ -+ return; -+} -+ -+static int calculate_time(int status) -+{ -+ union power_supply_propval charge_full, charge_empty; -+ union power_supply_propval charge, I; -+ -+ if (MPSY_PROP(CHARGE_FULL, &charge_full)) { -+ /* if battery can't report this property, use design value */ -+ if (MPSY_PROP(CHARGE_FULL_DESIGN, &charge_full)) -+ return -1; -+ } -+ -+ if (MPSY_PROP(CHARGE_EMPTY, &charge_empty)) { -+ /* if battery can't report this property, use design value */ -+ if (MPSY_PROP(CHARGE_EMPTY_DESIGN, &charge_empty)) -+ charge_empty.intval = 0; -+ } -+ -+ if (MPSY_PROP(CHARGE_AVG, &charge)) { -+ /* if battery can't report average value, use momentary */ -+ if (MPSY_PROP(CHARGE_NOW, &charge)) -+ return -1; -+ } -+ -+ if (MPSY_PROP(CURRENT_AVG, &I)) { -+ /* if battery can't report average value, use momentary */ -+ if (MPSY_PROP(CURRENT_NOW, &I)) -+ return -1; -+ } -+ -+ if (I.intval == 0) -+ return 0; -+ else if (status == POWER_SUPPLY_STATUS_CHARGING) -+ return ((charge.intval - charge_full.intval) * 60L) / -+ I.intval; -+ else -+ return -((charge.intval - charge_empty.intval) * 60L) / -+ I.intval; -+} -+ -+static int calculate_capacity(int using_charge) -+{ -+ enum power_supply_property full_prop, empty_prop; -+ enum power_supply_property full_design_prop, empty_design_prop; -+ enum power_supply_property now_prop, avg_prop; -+ union power_supply_propval empty, full, cur; -+ int ret; -+ -+ if (using_charge) { -+ full_prop = POWER_SUPPLY_PROP_CHARGE_FULL; -+ empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY; -+ full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN; -+ empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN; -+ now_prop = POWER_SUPPLY_PROP_CHARGE_NOW; -+ avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG; -+ } -+ else { -+ full_prop = POWER_SUPPLY_PROP_ENERGY_FULL; -+ empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY; -+ full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN; -+ empty_design_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN; -+ now_prop = POWER_SUPPLY_PROP_ENERGY_NOW; -+ avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG; -+ } -+ -+ if (_MPSY_PROP(full_prop, &full)) { -+ /* if battery can't report this property, use design value */ -+ if (_MPSY_PROP(full_design_prop, &full)) -+ return -1; -+ } -+ -+ if (_MPSY_PROP(avg_prop, &cur)) { -+ /* if battery can't report average value, use momentary */ -+ if (_MPSY_PROP(now_prop, &cur)) -+ return -1; -+ } -+ -+ if (_MPSY_PROP(empty_prop, &empty)) { -+ /* if battery can't report this property, use design value */ -+ if (_MPSY_PROP(empty_design_prop, &empty)) -+ empty.intval = 0; -+ } -+ -+ if (full.intval - empty.intval) -+ ret = ((cur.intval - empty.intval) * 100L) / -+ (full.intval - empty.intval); -+ else -+ return -1; -+ -+ if (ret > 100) -+ return 100; -+ else if (ret < 0) -+ return 0; -+ -+ return ret; -+} -+ -+static void apm_battery_apm_get_power_status(struct apm_power_info *info) -+{ -+ union power_supply_propval status; -+ union power_supply_propval capacity, time_to_full, time_to_empty; -+ -+ down(&power_supply_class->sem); -+ find_main_battery(); -+ if (!main_battery) { -+ up(&power_supply_class->sem); -+ return; -+ } -+ -+ /* status */ -+ -+ if (MPSY_PROP(STATUS, &status)) -+ status.intval = POWER_SUPPLY_STATUS_UNKNOWN; -+ -+ /* ac line status */ -+ -+ if ((status.intval == POWER_SUPPLY_STATUS_CHARGING) || -+ (status.intval == POWER_SUPPLY_STATUS_NOT_CHARGING) || -+ (status.intval == POWER_SUPPLY_STATUS_FULL)) -+ info->ac_line_status = APM_AC_ONLINE; -+ else -+ info->ac_line_status = APM_AC_OFFLINE; -+ -+ /* battery life (i.e. capacity, in percents) */ -+ -+ if (MPSY_PROP(CAPACITY, &capacity) == 0) -+ info->battery_life = capacity.intval; -+ else { -+ /* try calculate using energy */ -+ info->battery_life = calculate_capacity(0); -+ /* if failed try calculate using charge instead */ -+ if (info->battery_life == -1) -+ info->battery_life = calculate_capacity(1); -+ } -+ -+ /* charging status */ -+ -+ if (status.intval == POWER_SUPPLY_STATUS_CHARGING) -+ info->battery_status = APM_BATTERY_STATUS_CHARGING; -+ else { -+ if (info->battery_life > 50) -+ info->battery_status = APM_BATTERY_STATUS_HIGH; -+ else if (info->battery_life > 5) -+ info->battery_status = APM_BATTERY_STATUS_LOW; -+ else -+ info->battery_status = APM_BATTERY_STATUS_CRITICAL; -+ } -+ info->battery_flag = info->battery_status; -+ -+ /* time */ -+ -+ info->units = APM_UNITS_MINS; -+ -+ if (status.intval == POWER_SUPPLY_STATUS_CHARGING) { -+ if (MPSY_PROP(TIME_TO_FULL_AVG, &time_to_full)) { -+ if (MPSY_PROP(TIME_TO_FULL_NOW, &time_to_full)) -+ info->time = calculate_time(status.intval); -+ else -+ info->time = time_to_full.intval / 60; -+ } -+ } -+ else { -+ if (MPSY_PROP(TIME_TO_EMPTY_AVG, &time_to_empty)) { -+ if (MPSY_PROP(TIME_TO_EMPTY_NOW, &time_to_empty)) -+ info->time = calculate_time(status.intval); -+ else -+ info->time = time_to_empty.intval / 60; -+ } -+ } -+ -+ up(&power_supply_class->sem); -+ return; -+} -+ -+static int __init apm_battery_init(void) -+{ -+ printk(KERN_INFO "APM Battery Driver\n"); -+ -+ apm_get_power_status = apm_battery_apm_get_power_status; -+ return 0; -+} -+ -+static void __exit apm_battery_exit(void) -+{ -+ apm_get_power_status = NULL; -+ return; -+} -+ -+module_init(apm_battery_init); -+module_exit(apm_battery_exit); -+ -+MODULE_AUTHOR("Eugeny Boger <eugenyboger@dgap.mipt.ru>"); -+MODULE_DESCRIPTION("APM emulation driver for battery monitoring class"); -+MODULE_LICENSE("GPL"); -Index: linux-2.6.22/drivers/power/ds2760_battery.c -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/drivers/power/ds2760_battery.c 2007-08-23 12:13:52.000000000 +0200 -@@ -0,0 +1,475 @@ -+/* -+ * Driver for batteries with DS2760 chips inside. -+ * -+ * Copyright (c) 2007 Anton Vorontsov -+ * 2004-2007 Matt Reimer -+ * 2004 Szabolcs Gyurko -+ * -+ * Use consistent with the GNU GPL is permitted, -+ * provided that this copyright notice is -+ * preserved in its entirety in all copies and derived works. -+ * -+ * Author: Anton Vorontsov <cbou@mail.ru> -+ * February 2007 -+ * -+ * Matt Reimer <mreimer@vpop.net> -+ * April 2004, 2005, 2007 -+ * -+ * Szabolcs Gyurko <szabolcs.gyurko@tlt.hu> -+ * September 2004 -+ */ -+ -+#include <linux/module.h> -+#include <linux/param.h> -+#include <linux/jiffies.h> -+#include <linux/workqueue.h> -+#include <linux/pm.h> -+#include <linux/platform_device.h> -+#include <linux/power_supply.h> -+ -+#include "../w1/w1.h" -+#include "../w1/slaves/w1_ds2760.h" -+ -+struct ds2760_device_info { -+ struct device *dev; -+ -+ /* DS2760 data, valid after calling ds2760_battery_read_status() */ -+ unsigned long update_time; /* jiffies when data read */ -+ char raw[DS2760_DATA_SIZE]; /* raw DS2760 data */ -+ int voltage_raw; /* units of 4.88 mV */ -+ int voltage_uV; /* units of uV */ -+ int current_raw; /* units of 0.625 mA */ -+ int current_uA; /* units of uA */ -+ int accum_current_raw; /* units of 0.25 mAh */ -+ int accum_current_uAh; /* units of uAh */ -+ int temp_raw; /* units of 0.125 C */ -+ int temp_C; /* units of 0.1 C */ -+ int rated_capacity; /* units of uAh */ -+ int rem_capacity; /* percentage */ -+ int full_active_uAh; /* units of uAh */ -+ int empty_uAh; /* units of uAh */ -+ int life_sec; /* units of seconds */ -+ int charge_status; /* POWER_SUPPLY_STATUS_* */ -+ -+ int full_counter; -+ struct power_supply bat; -+ struct device *w1_dev; -+ struct workqueue_struct *monitor_wqueue; -+ struct delayed_work monitor_work; -+}; -+ -+static unsigned int cache_time = 1000; -+module_param(cache_time, uint, 0644); -+MODULE_PARM_DESC(cache_time, "cache time in milliseconds"); -+ -+/* Some batteries have their rated capacity stored a N * 10 mAh, while -+ * others use an index into this table. */ -+static int rated_capacities[] = { -+ 0, -+ 920, /* Samsung */ -+ 920, /* BYD */ -+ 920, /* Lishen */ -+ 920, /* NEC */ -+ 1440, /* Samsung */ -+ 1440, /* BYD */ -+ 1440, /* Lishen */ -+ 1440, /* NEC */ -+ 2880, /* Samsung */ -+ 2880, /* BYD */ -+ 2880, /* Lishen */ -+ 2880 /* NEC */ -+}; -+ -+/* array is level at temps 0C, 10C, 20C, 30C, 40C -+ * temp is in Celsius */ -+static int battery_interpolate(int array[], int temp) -+{ -+ int index, dt; -+ -+ if (temp <= 0) -+ return array[0]; -+ if (temp >= 40) -+ return array[4]; -+ -+ index = temp / 10; -+ dt = temp % 10; -+ -+ return array[index] + (((array[index + 1] - array[index]) * dt) / 10); -+} -+ -+static int ds2760_battery_read_status(struct ds2760_device_info *di) -+{ -+ int ret, i, start, count, scale[5]; -+ -+ if (di->update_time && time_before(jiffies, di->update_time + -+ msecs_to_jiffies(cache_time))) -+ return 0; -+ -+ /* The first time we read the entire contents of SRAM/EEPROM, -+ * but after that we just read the interesting bits that change. */ -+ if (di->update_time == 0) { -+ start = 0; -+ count = DS2760_DATA_SIZE; -+ } -+ else { -+ start = DS2760_VOLTAGE_MSB; -+ count = DS2760_TEMP_LSB - start + 1; -+ } -+ -+ ret = w1_ds2760_read(di->w1_dev, di->raw + start, start, count); -+ if (ret != count) { -+ dev_warn(di->dev, "call to w1_ds2760_read failed (0x%p)\n", -+ di->w1_dev); -+ return 1; -+ } -+ -+ di->update_time = jiffies; -+ -+ /* DS2760 reports voltage in units of 4.88mV, but the battery class -+ * reports in units of uV, so convert by multiplying by 4880. */ -+ di->voltage_raw = (di->raw[DS2760_VOLTAGE_MSB] << 3) | -+ (di->raw[DS2760_VOLTAGE_LSB] >> 5); -+ di->voltage_uV = di->voltage_raw * 4880; -+ -+ /* DS2760 reports current in signed units of 0.625mA, but the battery -+ * class reports in units of uA, so convert by multiplying by 625. */ -+ di->current_raw = -+ (((signed char)di->raw[DS2760_CURRENT_MSB]) << 5) | -+ (di->raw[DS2760_CURRENT_LSB] >> 3); -+ di->current_uA = di->current_raw * 625; -+ -+ /* DS2760 reports accumulated current in signed units of 0.25mAh. */ -+ di->accum_current_raw = -+ (((signed char)di->raw[DS2760_CURRENT_ACCUM_MSB]) << 8) | -+ di->raw[DS2760_CURRENT_ACCUM_LSB]; -+ di->accum_current_uAh = di->accum_current_raw * 250; -+ -+ /* DS2760 reports temperature in signed units of 0.125C, but the -+ * battery class reports in units of 1/10 C, so we convert by -+ * multiplying by .125 * 10 = 1.25. */ -+ di->temp_raw = (((signed char)di->raw[DS2760_TEMP_MSB]) << 3) | -+ (di->raw[DS2760_TEMP_LSB] >> 5); -+ di->temp_C = di->temp_raw + (di->temp_raw / 4); -+ -+ /* At least some battery monitors (e.g. HP iPAQ) store the battery's -+ * maximum rated capacity. */ -+ if (di->raw[DS2760_RATED_CAPACITY] < ARRAY_SIZE(rated_capacities)) -+ di->rated_capacity = rated_capacities[ -+ (unsigned int)di->raw[DS2760_RATED_CAPACITY]]; -+ else -+ di->rated_capacity = di->raw[DS2760_RATED_CAPACITY] * 10; -+ -+ di->rated_capacity *= 1000; /* convert to uAh */ -+ -+ /* Calculate the full level at the present temperature. */ -+ di->full_active_uAh = di->raw[DS2760_ACTIVE_FULL] << 8 | -+ di->raw[DS2760_ACTIVE_FULL + 1]; -+ -+ scale[0] = di->raw[DS2760_ACTIVE_FULL] << 8 | -+ di->raw[DS2760_ACTIVE_FULL + 1]; -+ for (i = 1; i < 5; i++) -+ scale[i] = scale[i - 1] + di->raw[DS2760_ACTIVE_FULL + 2 + i]; -+ -+ di->full_active_uAh = battery_interpolate(scale, di->temp_C / 10); -+ di->full_active_uAh *= 1000; /* convert to uAh */ -+ -+ /* Calculate the empty level at the present temperature. */ -+ scale[4] = di->raw[DS2760_ACTIVE_EMPTY + 4]; -+ for (i = 3; i >= 0; i--) -+ scale[i] = scale[i + 1] + di->raw[DS2760_ACTIVE_EMPTY + i]; -+ -+ di->empty_uAh = battery_interpolate(scale, di->temp_C / 10); -+ di->empty_uAh *= 1000; /* convert to uAh */ -+ -+ /* From Maxim Application Note 131: remaining capacity = -+ * ((ICA - Empty Value) / (Full Value - Empty Value)) x 100% */ -+ di->rem_capacity = ((di->accum_current_uAh - di->empty_uAh) * 100L) / -+ (di->full_active_uAh - di->empty_uAh); -+ -+ if (di->rem_capacity < 0) -+ di->rem_capacity = 0; -+ if (di->rem_capacity > 100) -+ di->rem_capacity = 100; -+ -+ if (di->current_uA) -+ di->life_sec = -((di->accum_current_uAh - di->empty_uAh) * -+ 3600L) / di->current_uA; -+ else -+ di->life_sec = 0; -+ -+ return 0; -+} -+ -+static void ds2760_battery_update_status(struct ds2760_device_info *di) -+{ -+ int old_charge_status = di->charge_status; -+ -+ ds2760_battery_read_status(di); -+ -+ if (di->charge_status == POWER_SUPPLY_STATUS_UNKNOWN) -+ di->full_counter = 0; -+ -+ if (power_supply_am_i_supplied(&di->bat)) { -+ if (di->current_uA > 10000) { -+ di->charge_status = POWER_SUPPLY_STATUS_CHARGING; -+ di->full_counter = 0; -+ } -+ else if (di->current_uA < -5000) { -+ if (di->charge_status != POWER_SUPPLY_STATUS_NOT_CHARGING) -+ dev_notice(di->dev, "not enough power to " -+ "charge\n"); -+ di->charge_status = POWER_SUPPLY_STATUS_NOT_CHARGING; -+ di->full_counter = 0; -+ } -+ else if (di->current_uA < 10000 && -+ di->charge_status != POWER_SUPPLY_STATUS_FULL) { -+ -+ /* Don't consider the battery to be full unless -+ * we've seen the current < 10 mA at least two -+ * consecutive times. */ -+ -+ di->full_counter++; -+ -+ if (di->full_counter < 2) -+ di->charge_status = POWER_SUPPLY_STATUS_CHARGING; -+ else { -+ unsigned char acr[2]; -+ int acr_val; -+ -+ /* acr is in units of 0.25 mAh */ -+ acr_val = di->full_active_uAh * 4L / 1000; -+ -+ acr[0] = acr_val >> 8; -+ acr[1] = acr_val & 0xff; -+ -+ if (w1_ds2760_write(di->w1_dev, acr, -+ DS2760_CURRENT_ACCUM_MSB, 2) < 2) -+ dev_warn(di->dev, -+ "ACR reset failed\n"); -+ -+ di->charge_status = POWER_SUPPLY_STATUS_FULL; -+ } -+ } -+ } -+ else { -+ di->charge_status = POWER_SUPPLY_STATUS_DISCHARGING; -+ di->full_counter = 0; -+ } -+ -+ if (di->charge_status != old_charge_status) -+ power_supply_changed(&di->bat); -+ -+ return; -+} -+ -+static void ds2760_battery_work(struct work_struct *work) -+{ -+ struct ds2760_device_info *di = container_of(work, -+ struct ds2760_device_info, monitor_work.work); -+ const int interval = HZ * 60; -+ -+ dev_dbg(di->dev, "%s\n", __FUNCTION__); -+ -+ ds2760_battery_update_status(di); -+ queue_delayed_work(di->monitor_wqueue, &di->monitor_work, interval); -+ -+ return; -+} -+ -+#define to_ds2760_device_info(x) container_of((x), struct ds2760_device_info, \ -+ bat); -+ -+static void ds2760_battery_external_power_changed(struct power_supply *psy) -+{ -+ struct ds2760_device_info *di = to_ds2760_device_info(psy); -+ -+ dev_dbg(di->dev, "%s\n", __FUNCTION__); -+ -+ cancel_delayed_work(&di->monitor_work); -+ queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ/10); -+ -+ return; -+} -+ -+static int ds2760_battery_get_property(struct power_supply *psy, -+ enum power_supply_property psp, -+ union power_supply_propval *val) -+{ -+ struct ds2760_device_info *di = to_ds2760_device_info(psy); -+ -+ switch (psp) { -+ case POWER_SUPPLY_PROP_STATUS: -+ val->intval = di->charge_status; -+ return 0; -+ default: -+ break; -+ } -+ -+ ds2760_battery_read_status(di); -+ -+ switch (psp) { -+ case POWER_SUPPLY_PROP_VOLTAGE_NOW: -+ val->intval = di->voltage_uV; -+ break; -+ case POWER_SUPPLY_PROP_CURRENT_NOW: -+ val->intval = di->current_uA; -+ break; -+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: -+ val->intval = di->rated_capacity; -+ break; -+ case POWER_SUPPLY_PROP_CHARGE_FULL: -+ val->intval = di->full_active_uAh; -+ break; -+ case POWER_SUPPLY_PROP_CHARGE_EMPTY: -+ val->intval = di->empty_uAh; -+ break; -+ case POWER_SUPPLY_PROP_CHARGE_NOW: -+ val->intval = di->accum_current_uAh; -+ break; -+ case POWER_SUPPLY_PROP_TEMP: -+ val->intval = di->temp_C; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static enum power_supply_property ds2760_battery_props[] = { -+ POWER_SUPPLY_PROP_STATUS, -+ POWER_SUPPLY_PROP_VOLTAGE_NOW, -+ POWER_SUPPLY_PROP_CURRENT_NOW, -+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, -+ POWER_SUPPLY_PROP_CHARGE_FULL, -+ POWER_SUPPLY_PROP_CHARGE_EMPTY, -+ POWER_SUPPLY_PROP_CHARGE_NOW, -+ POWER_SUPPLY_PROP_TEMP, -+}; -+ -+static int ds2760_battery_probe(struct platform_device *pdev) -+{ -+ int retval = 0; -+ struct ds2760_device_info *di; -+ struct ds2760_platform_data *pdata; -+ -+ di = kzalloc(sizeof(*di), GFP_KERNEL); -+ if (!di) { -+ retval = -ENOMEM; -+ goto di_alloc_failed; -+ } -+ -+ platform_set_drvdata(pdev, di); -+ -+ pdata = pdev->dev.platform_data; -+ di->dev = &pdev->dev; -+ di->w1_dev = pdev->dev.parent; -+ di->bat.name = pdev->dev.bus_id; -+ di->bat.type = POWER_SUPPLY_TYPE_BATTERY; -+ di->bat.properties = ds2760_battery_props; -+ di->bat.num_properties = ARRAY_SIZE(ds2760_battery_props); -+ di->bat.get_property = ds2760_battery_get_property; -+ di->bat.external_power_changed = -+ ds2760_battery_external_power_changed; -+ di->bat.use_for_apm = 1; -+ -+ di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN; -+ -+ retval = power_supply_register(&pdev->dev, &di->bat); -+ if (retval) { -+ dev_err(di->dev, "failed to register battery"); -+ goto batt_failed; -+ } -+ -+ INIT_DELAYED_WORK(&di->monitor_work, ds2760_battery_work); -+ di->monitor_wqueue = create_singlethread_workqueue(pdev->dev.bus_id); -+ if (!di->monitor_wqueue) { -+ retval = -ESRCH; -+ goto workqueue_failed; -+ } -+ queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ * 1); -+ -+ goto success; -+ -+workqueue_failed: -+ power_supply_unregister(&di->bat); -+batt_failed: -+ kfree(di); -+di_alloc_failed: -+success: -+ return retval; -+} -+ -+static int ds2760_battery_remove(struct platform_device *pdev) -+{ -+ struct ds2760_device_info *di = platform_get_drvdata(pdev); -+ -+ cancel_rearming_delayed_workqueue(di->monitor_wqueue, -+ &di->monitor_work); -+ destroy_workqueue(di->monitor_wqueue); -+ power_supply_unregister(&di->bat); -+ -+ return 0; -+} -+ -+#ifdef CONFIG_PM -+ -+static int ds2760_battery_suspend(struct platform_device *pdev, -+ pm_message_t state) -+{ -+ struct ds2760_device_info *di = platform_get_drvdata(pdev); -+ -+ di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN; -+ -+ return 0; -+} -+ -+static int ds2760_battery_resume(struct platform_device *pdev) -+{ -+ struct ds2760_device_info *di = platform_get_drvdata(pdev); -+ -+ di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN; -+ power_supply_changed(&di->bat); -+ -+ cancel_delayed_work(&di->monitor_work); -+ queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ); -+ -+ return 0; -+} -+ -+#else -+ -+#define ds2760_battery_suspend NULL -+#define ds2760_battery_resume NULL -+ -+#endif /* CONFIG_PM */ -+ -+static struct platform_driver ds2760_battery_driver = { -+ .driver = { -+ .name = "ds2760-battery", -+ }, -+ .probe = ds2760_battery_probe, -+ .remove = ds2760_battery_remove, -+ .suspend = ds2760_battery_suspend, -+ .resume = ds2760_battery_resume, -+}; -+ -+static int __init ds2760_battery_init(void) -+{ -+ return platform_driver_register(&ds2760_battery_driver); -+} -+ -+static void __exit ds2760_battery_exit(void) -+{ -+ platform_driver_unregister(&ds2760_battery_driver); -+ return; -+} -+ -+module_init(ds2760_battery_init); -+module_exit(ds2760_battery_exit); -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>, " -+ "Matt Reimer <mreimer@vpop.net>, " -+ "Anton Vorontsov <cbou@mail.ru>"); -+MODULE_DESCRIPTION("ds2760 battery driver"); -Index: linux-2.6.22/drivers/power/Kconfig -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/drivers/power/Kconfig 2007-08-23 12:13:52.000000000 +0200 -@@ -0,0 +1,70 @@ -+menuconfig POWER_SUPPLY -+ tristate "Power supply class support" -+ help -+ Say Y here to enable power supply class support. This allows -+ power supply (batteries, AC, USB) monitoring by userspace -+ via sysfs and uevent (if available) and/or APM kernel interface -+ (if selected below). -+ -+if POWER_SUPPLY -+ -+config POWER_SUPPLY_DEBUG -+ bool "Power supply debug" -+ help -+ Say Y here to enable debugging messages for power supply class -+ and drivers. -+ -+config PDA_POWER -+ tristate "Generic PDA/phone power driver" -+ help -+ Say Y here to enable generic power driver for PDAs and phones with -+ one or two external power supplies (AC/USB) connected to main and -+ backup batteries, and optional builtin charger. -+ -+config APM_POWER -+ tristate "APM emulation for class batteries" -+ depends on APM_EMULATION -+ help -+ Say Y here to enable support APM status emulation using -+ battery class devices. -+ -+config BATTERY_DS2760 -+ tristate "DS2760 battery driver (HP iPAQ & others)" -+ select W1 -+ select W1_SLAVE_DS2760 -+ help -+ Say Y here to enable support for batteries with ds2760 chip. -+ -+config BATTERY_PMU -+ tristate "Apple PMU battery" -+ depends on ADB_PMU -+ help -+ Say Y here to expose battery information on Apple machines -+ through the generic battery class. -+ -+config BATTERY_OLPC -+ tristate "One Laptop Per Child battery" -+ depends on X86_32 -+ help -+ Say Y to enable support for the battery on the OLPC laptop. -+ -+# drivers below are not in battery2-2.6 tree -+ -+config ADC_BATTERY -+ tristate "Generic ADC battery driver" -+ depends on ADC && POWER_SUPPLY -+ help -+ Say Y here to enable support for battery monitoring using generic ADC device. -+ -+config IPAQ_MICRO_BATTERY -+ tristate "HP iPAQ Micro ASIC battery driver" -+ depends on IPAQ_MICRO && POWER_SUPPLY -+ help -+ Choose this option if you want to monitor battery status on -+ Compaq/HP iPAQ h3100 h3600 -+ -+config MCP_UCB1x00_SIMPAD_BATTERY -+ tristate "SIMpad Battery Reading Support" -+ depends on MCP_UCB1x00 && POWER_SUPPLY -+ -+endif # POWER_SUPPLY -Index: linux-2.6.22/drivers/power/Makefile -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/drivers/power/Makefile 2007-08-23 12:13:52.000000000 +0200 -@@ -0,0 +1,28 @@ -+power_supply-objs := power_supply_core.o -+ -+ifeq ($(CONFIG_SYSFS),y) -+power_supply-objs += power_supply_sysfs.o -+endif -+ -+ifeq ($(CONFIG_LEDS_TRIGGERS),y) -+power_supply-objs += power_supply_leds.o -+endif -+ -+ifeq ($(CONFIG_POWER_SUPPLY_DEBUG),y) -+EXTRA_CFLAGS += -DDEBUG -+endif -+ -+obj-$(CONFIG_POWER_SUPPLY) += power_supply.o -+ -+obj-$(CONFIG_PDA_POWER) += pda_power.o -+obj-$(CONFIG_APM_POWER) += apm_power.o -+ -+obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o -+obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o -+obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o -+ -+# drivers below are not in battery2-2.6 tree -+ -+obj-$(CONFIG_ADC_BATTERY) += adc_battery.o -+obj-$(CONFIG_IPAQ_MICRO_BATTERY) += micro_battery.o -+obj-$(CONFIG_MCP_UCB1x00_SIMPAD_BATTERY) += simpad-battery.o -Index: linux-2.6.22/drivers/power/micro_battery.c -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/drivers/power/micro_battery.c 2007-08-23 12:25:20.000000000 +0200 -@@ -0,0 +1,257 @@ -+/* -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ * -+ * h3600 atmel micro companion support, battery subdevice -+ * based on previous kernel 2.4 version -+ * Author : Alessandro Gardich <gremlin@gremlin.it> -+ * -+ */ -+ -+ -+#include <linux/module.h> -+#include <linux/version.h> -+ -+#include <linux/init.h> -+#include <linux/fs.h> -+#include <linux/interrupt.h> -+#include <linux/sched.h> -+#include <linux/pm.h> -+#include <linux/sysctl.h> -+#include <linux/proc_fs.h> -+#include <linux/delay.h> -+#include <linux/device.h> -+#include <linux/power_supply.h> -+#include <linux/platform_device.h> -+#include <linux/timer.h> -+ -+#include <asm/arch/hardware.h> -+ -+#include <asm/arch/h3600.h> -+#include <asm/arch/SA-1100.h> -+ -+#include <asm/hardware/micro.h> -+ -+#define BATT_PERIOD 10*HZ -+ -+#define H3600_BATT_STATUS_HIGH 0x01 -+#define H3600_BATT_STATUS_LOW 0x02 -+#define H3600_BATT_STATUS_CRITICAL 0x04 -+#define H3600_BATT_STATUS_CHARGING 0x08 -+#define H3600_BATT_STATUS_CHARGEMAIN 0x10 -+#define H3600_BATT_STATUS_DEAD 0x20 /* Battery will not charge */ -+#define H3600_BATT_STATUS_NOTINSTALLED 0x20 /* For expansion pack batteries */ -+#define H3600_BATT_STATUS_FULL 0x40 /* Battery fully charged (and connected to AC) */ -+#define H3600_BATT_STATUS_NOBATTERY 0x80 -+#define H3600_BATT_STATUS_UNKNOWN 0xff -+ -+ -+//static struct power_supply_dev *micro_battery; -+ -+static micro_private_t *p_micro; -+ -+struct timer_list batt_timer; -+ -+struct { -+ int ac; -+ int update_time; -+ int chemistry; -+ int voltage; -+ int temperature; -+ int flag; -+} micro_battery; -+ -+static void micro_battery_receive (int len, unsigned char *data) { -+ if (0) { -+ printk(KERN_ERR "h3600_battery - AC = %02x\n", data[0]); -+ printk(KERN_ERR "h3600_battery - BAT1 chemistry = %02x\n", data[1]); -+ printk(KERN_ERR "h3600_battery - BAT1 voltage = %d %02x%02x\n", (data[3]<<8)+data[2], data[2], data[3]); -+ printk(KERN_ERR "h3600_battery - BAT1 status = %02x\n", data[4]); -+ } -+ -+ micro_battery.ac = data[0]; -+ micro_battery.chemistry = data[1]; -+ micro_battery.voltage = ((((unsigned short)data[3]<<8)+data[2]) * 5000L ) * 1000 / 1024; -+ micro_battery.flag = data[4]; -+ -+ if (len == 9) { -+ if (0) { -+ printk(KERN_ERR "h3600_battery - BAT2 chemistry = %02x\n", data[5]); -+ printk(KERN_ERR "h3600_battery - BAT2 voltage = %d %02x%02x\n", (data[7]<<8)+data[6], data[6], data[7]); -+ printk(KERN_ERR "h3600_battery - BAT2 status = %02x\n", data[8]); -+ } -+ } -+} -+ -+static void micro_temperature_receive (int len, unsigned char *data) { -+ micro_battery.temperature = ((unsigned short)data[1]<<8)+data[0]; -+} -+ -+void h3600_battery_read_status(unsigned long data) { -+ -+ if (++data % 2) -+ h3600_micro_tx_msg(0x09,0,NULL); -+ else -+ h3600_micro_tx_msg(0x06,0,NULL); -+ -+ batt_timer.expires += BATT_PERIOD; -+ batt_timer.data = data; -+ -+ add_timer(&batt_timer); -+} -+ -+int get_capacity(struct power_supply *b) { -+ switch (micro_battery.flag) { -+ case H3600_BATT_STATUS_HIGH : return 100; break; -+ case H3600_BATT_STATUS_LOW : return 50; break; -+ case H3600_BATT_STATUS_CRITICAL : return 5; break; -+ default: break; -+ } -+ return 0; -+} -+ -+int get_status(struct power_supply *b) { -+ -+ if (micro_battery.flag == H3600_BATT_STATUS_UNKNOWN) -+ return POWER_SUPPLY_STATUS_UNKNOWN; -+ -+ if (micro_battery.flag & H3600_BATT_STATUS_FULL) -+ return POWER_SUPPLY_STATUS_FULL; -+ -+ if ((micro_battery.flag & H3600_BATT_STATUS_CHARGING) || -+ (micro_battery.flag & H3600_BATT_STATUS_CHARGEMAIN)) -+ return POWER_SUPPLY_STATUS_CHARGING; -+ -+ return POWER_SUPPLY_STATUS_DISCHARGING; -+} -+ -+static int micro_batt_get_property(struct power_supply *b, -+ enum power_supply_property psp, -+ union power_supply_propval *val) -+{ -+ switch (psp) { -+ case POWER_SUPPLY_PROP_STATUS: -+ val->intval = get_status(b); -+ break; -+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: -+ val->intval = 4700000; -+ break; -+ case POWER_SUPPLY_PROP_CAPACITY: -+ val->intval = get_capacity(b); -+ break; -+ case POWER_SUPPLY_PROP_TEMP: -+ val->intval = micro_battery.temperature; -+ break; -+ case POWER_SUPPLY_PROP_VOLTAGE_NOW: -+ val->intval = micro_battery.voltage; -+ break; -+ default: -+ return -EINVAL; -+ }; -+ -+ return 0; -+} -+ -+static enum power_supply_property micro_batt_props[] = { -+ POWER_SUPPLY_PROP_STATUS, -+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, -+ POWER_SUPPLY_PROP_CAPACITY, -+ POWER_SUPPLY_PROP_TEMP, -+ POWER_SUPPLY_PROP_VOLTAGE_NOW, -+}; -+ -+static struct power_supply h3600_battery = { -+ .name = "main-battery", -+ .properties = micro_batt_props, -+ .num_properties = ARRAY_SIZE(micro_batt_props), -+ .get_property = micro_batt_get_property, -+ .use_for_apm = 1, -+}; -+ -+static int micro_batt_probe (struct platform_device *pdev) -+{ -+ if (1) printk(KERN_ERR "micro battery probe : begin\n"); -+ -+ power_supply_register(&pdev->dev, &h3600_battery); -+ -+ { /*--- callback ---*/ -+ p_micro = platform_get_drvdata(pdev); -+ spin_lock(p_micro->lock); -+ p_micro->h_batt = micro_battery_receive; -+ p_micro->h_temp = micro_temperature_receive; -+ spin_unlock(p_micro->lock); -+ } -+ -+ { /*--- timer ---*/ -+ init_timer(&batt_timer); -+ batt_timer.expires = jiffies + BATT_PERIOD; -+ batt_timer.data = 0; -+ batt_timer.function = h3600_battery_read_status; -+ -+ add_timer(&batt_timer); -+ } -+ -+ if (1) printk(KERN_ERR "micro battery probe : end\n"); -+ return 0; -+} -+ -+static int micro_batt_remove (struct platform_device *pdev) -+{ -+ power_supply_unregister(&h3600_battery); -+ { /*--- callback ---*/ -+ init_timer(&batt_timer); -+ p_micro->h_batt = NULL; -+ p_micro->h_temp = NULL; -+ spin_unlock(p_micro->lock); -+ } -+ { /*--- timer ---*/ -+ del_timer_sync(&batt_timer); -+ } -+ return 0; -+} -+ -+static int micro_batt_suspend ( struct platform_device *pdev, pm_message_t state) -+{ -+ { /*--- timer ---*/ -+ del_timer(&batt_timer); -+ } -+ return 0; -+} -+ -+static int micro_batt_resume ( struct platform_device *pdev) -+{ -+ { /*--- timer ---*/ -+ add_timer(&batt_timer); -+ } -+ return 0; -+} -+ -+struct platform_driver micro_batt_device_driver = { -+ .driver = { -+ .name = "h3600-micro-battery", -+ }, -+ .probe = micro_batt_probe, -+ .remove = micro_batt_remove, -+ .suspend = micro_batt_suspend, -+ .resume = micro_batt_resume, -+}; -+ -+static int micro_batt_init (void) -+{ -+ return platform_driver_register(µ_batt_device_driver); -+} -+ -+static void micro_batt_cleanup (void) -+{ -+ platform_driver_unregister (µ_batt_device_driver); -+} -+ -+module_init (micro_batt_init); -+module_exit (micro_batt_cleanup); -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("gremlin.it"); -+MODULE_DESCRIPTION("driver for iPAQ Atmel micro battery"); -+ -+ -Index: linux-2.6.22/drivers/power/olpc_battery.c -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/drivers/power/olpc_battery.c 2007-08-23 12:13:52.000000000 +0200 -@@ -0,0 +1,302 @@ -+/* -+ * Battery driver for One Laptop Per Child board. -+ * -+ * Copyright © 2006 David Woodhouse <dwmw2@infradead.org> -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include <linux/module.h> -+#include <linux/err.h> -+#include <linux/platform_device.h> -+#include <linux/power_supply.h> -+#include <linux/jiffies.h> -+#include <linux/sched.h> -+#include <asm/io.h> -+ -+#define wBAT_VOLTAGE 0xf900 /* *9.76/32, mV */ -+#define wBAT_CURRENT 0xf902 /* *15.625/120, mA */ -+#define wBAT_TEMP 0xf906 /* *256/1000, °C */ -+#define wAMB_TEMP 0xf908 /* *256/1000, °C */ -+#define SOC 0xf910 /* percentage */ -+#define sMBAT_STATUS 0xfaa4 -+#define sBAT_PRESENT 1 -+#define sBAT_FULL 2 -+#define sBAT_DESTROY 4 /* what is this exactly? */ -+#define sBAT_LOW 32 -+#define sBAT_DISCHG 64 -+#define sMCHARGE_STATUS 0xfaa5 -+#define sBAT_CHARGE 1 -+#define sBAT_OVERTEMP 4 -+#define sBAT_NiMH 8 -+#define sPOWER_FLAG 0xfa40 -+#define ADAPTER_IN 1 -+ -+/********************************************************************* -+ * EC locking and access -+ *********************************************************************/ -+ -+static int lock_ec(void) -+{ -+ unsigned long timeo = jiffies + HZ / 20; -+ -+ while (1) { -+ unsigned char lock = inb(0x6c) & 0x80; -+ if (!lock) -+ return 0; -+ if (time_after(jiffies, timeo)) { -+ printk(KERN_ERR "olpc_battery: failed to lock EC for " -+ "battery access\n"); -+ return 1; -+ } -+ yield(); -+ } -+} -+ -+static void unlock_ec(void) -+{ -+ outb(0xff, 0x6c); -+ return; -+} -+ -+static unsigned char read_ec_byte(unsigned short adr) -+{ -+ outb(adr >> 8, 0x381); -+ outb(adr, 0x382); -+ return inb(0x383); -+} -+ -+static unsigned short read_ec_word(unsigned short adr) -+{ -+ return (read_ec_byte(adr) << 8) | read_ec_byte(adr + 1); -+} -+ -+/********************************************************************* -+ * Power -+ *********************************************************************/ -+ -+static int olpc_ac_get_prop(struct power_supply *psy, -+ enum power_supply_property psp, -+ union power_supply_propval *val) -+{ -+ int ret = 0; -+ -+ if (lock_ec()) -+ return -EIO; -+ -+ switch (psp) { -+ case POWER_SUPPLY_PROP_ONLINE: -+ if (!(read_ec_byte(sMBAT_STATUS) & sBAT_PRESENT)) { -+ ret = -ENODEV; -+ goto out; -+ } -+ val->intval = !!(read_ec_byte(sPOWER_FLAG) & ADAPTER_IN); -+ break; -+ default: -+ ret = -EINVAL; -+ break; -+ } -+out: -+ unlock_ec(); -+ return ret; -+} -+ -+static enum power_supply_property olpc_ac_props[] = { -+ POWER_SUPPLY_PROP_ONLINE, -+}; -+ -+static struct power_supply olpc_ac = { -+ .name = "olpc-ac", -+ .type = POWER_SUPPLY_TYPE_MAINS, -+ .properties = olpc_ac_props, -+ .num_properties = ARRAY_SIZE(olpc_ac_props), -+ .get_property = olpc_ac_get_prop, -+}; -+ -+/********************************************************************* -+ * Battery properties -+ *********************************************************************/ -+ -+static int olpc_bat_get_property(struct power_supply *psy, -+ enum power_supply_property psp, -+ union power_supply_propval *val) -+{ -+ int ret = 0; -+ -+ if (lock_ec()) -+ return -EIO; -+ -+ switch (psp) { -+ case POWER_SUPPLY_PROP_STATUS: -+ { -+ int status = POWER_SUPPLY_STATUS_UNKNOWN; -+ -+ val->intval = read_ec_byte(sMBAT_STATUS); -+ -+ if (!(val->intval & sBAT_PRESENT)) { -+ ret = -ENODEV; -+ goto out; -+ } -+ -+ if (val->intval & sBAT_DISCHG) -+ status = POWER_SUPPLY_STATUS_DISCHARGING; -+ else if (val->intval & sBAT_FULL) -+ status = POWER_SUPPLY_STATUS_FULL; -+ -+ val->intval = read_ec_byte(sMCHARGE_STATUS); -+ if (val->intval & sBAT_CHARGE) -+ status = POWER_SUPPLY_STATUS_CHARGING; -+ -+ val->intval = status; -+ break; -+ } -+ case POWER_SUPPLY_PROP_PRESENT: -+ val->intval = !!(read_ec_byte(sMBAT_STATUS) & sBAT_PRESENT); -+ break; -+ case POWER_SUPPLY_PROP_HEALTH: -+ val->intval = read_ec_byte(sMCHARGE_STATUS); -+ if (val->intval & sBAT_OVERTEMP) -+ val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; -+ else -+ val->intval = POWER_SUPPLY_HEALTH_GOOD; -+ break; -+ case POWER_SUPPLY_PROP_TECHNOLOGY: -+ val->intval = read_ec_byte(sMCHARGE_STATUS); -+ if (val->intval & sBAT_NiMH) -+ val->intval = POWER_SUPPLY_TECHNOLOGY_NIMH; -+ else -+ val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; -+ break; -+ case POWER_SUPPLY_PROP_VOLTAGE_AVG: -+ val->intval = read_ec_byte(wBAT_VOLTAGE) * 9760L / 32; -+ break; -+ case POWER_SUPPLY_PROP_CURRENT_AVG: -+ val->intval = read_ec_byte(wBAT_CURRENT) * 15625L / 120; -+ break; -+ case POWER_SUPPLY_PROP_CAPACITY: -+ val->intval = read_ec_byte(SOC); -+ break; -+ case POWER_SUPPLY_PROP_CAPACITY_LEVEL: -+ val->intval = read_ec_byte(sMBAT_STATUS); -+ if (val->intval & sBAT_FULL) -+ val->intval = POWER_SUPPLY_CAPACITY_LEVEL_FULL; -+ else if (val->intval & sBAT_LOW) -+ val->intval = POWER_SUPPLY_CAPACITY_LEVEL_LOW; -+ else -+ val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; -+ break; -+ case POWER_SUPPLY_PROP_TEMP: -+ val->intval = read_ec_byte(wBAT_TEMP) * 256 / 100; -+ break; -+ case POWER_SUPPLY_PROP_TEMP_AMBIENT: -+ val->intval = read_ec_byte(wAMB_TEMP) * 256 / 100; -+ break; -+ default: -+ ret = -EINVAL; -+ break; -+ } -+ -+out: -+ unlock_ec(); -+ return ret; -+} -+ -+static enum power_supply_property olpc_bat_props[] = { -+ POWER_SUPPLY_PROP_STATUS, -+ POWER_SUPPLY_PROP_PRESENT, -+ POWER_SUPPLY_PROP_HEALTH, -+ POWER_SUPPLY_PROP_TECHNOLOGY, -+ POWER_SUPPLY_PROP_VOLTAGE_AVG, -+ POWER_SUPPLY_PROP_CURRENT_AVG, -+ POWER_SUPPLY_PROP_CAPACITY, -+ POWER_SUPPLY_PROP_CAPACITY_LEVEL, -+ POWER_SUPPLY_PROP_TEMP, -+ POWER_SUPPLY_PROP_TEMP_AMBIENT, -+}; -+ -+/********************************************************************* -+ * Initialisation -+ *********************************************************************/ -+ -+static struct platform_device *bat_pdev; -+ -+static struct power_supply olpc_bat = { -+ .properties = olpc_bat_props, -+ .num_properties = ARRAY_SIZE(olpc_bat_props), -+ .get_property = olpc_bat_get_property, -+ .use_for_apm = 1, -+}; -+ -+static int __init olpc_bat_init(void) -+{ -+ int ret = 0; -+ unsigned short tmp; -+ -+ if (!request_region(0x380, 4, "olpc-battery")) { -+ ret = -EIO; -+ goto region_failed; -+ } -+ -+ if (lock_ec()) { -+ ret = -EIO; -+ goto lock_failed; -+ } -+ -+ tmp = read_ec_word(0xfe92); -+ unlock_ec(); -+ -+ if (tmp != 0x380) { -+ /* Doesn't look like OLPC EC */ -+ ret = -ENODEV; -+ goto not_olpc_ec; -+ } -+ -+ bat_pdev = platform_device_register_simple("olpc-battery", 0, NULL, 0); -+ if (IS_ERR(bat_pdev)) { -+ ret = PTR_ERR(bat_pdev); -+ goto pdev_failed; -+ } -+ -+ ret = power_supply_register(&bat_pdev->dev, &olpc_ac); -+ if (ret) -+ goto ac_failed; -+ -+ olpc_bat.name = bat_pdev->name; -+ -+ ret = power_supply_register(&bat_pdev->dev, &olpc_bat); -+ if (ret) -+ goto battery_failed; -+ -+ goto success; -+ -+battery_failed: -+ power_supply_unregister(&olpc_ac); -+ac_failed: -+ platform_device_unregister(bat_pdev); -+pdev_failed: -+not_olpc_ec: -+lock_failed: -+ release_region(0x380, 4); -+region_failed: -+success: -+ return ret; -+} -+ -+static void __exit olpc_bat_exit(void) -+{ -+ power_supply_unregister(&olpc_bat); -+ power_supply_unregister(&olpc_ac); -+ platform_device_unregister(bat_pdev); -+ release_region(0x380, 4); -+ return; -+} -+ -+module_init(olpc_bat_init); -+module_exit(olpc_bat_exit); -+ -+MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("Battery driver for One Laptop Per Child " -+ "($100 laptop) board."); -Index: linux-2.6.22/drivers/power/pda_power.c -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/drivers/power/pda_power.c 2007-08-23 12:13:52.000000000 +0200 -@@ -0,0 +1,263 @@ -+/* -+ * Common power driver for PDAs and phones with one or two external -+ * power supplies (AC/USB) connected to main and backup batteries, -+ * and optional builtin charger. -+ * -+ * Copyright 2007 Anton Vorontsov <cbou@mail.ru> -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include <linux/module.h> -+#include <linux/platform_device.h> -+#include <linux/interrupt.h> -+#include <linux/power_supply.h> -+#include <linux/pda_power.h> -+#include <linux/timer.h> -+#include <linux/jiffies.h> -+ -+static inline unsigned int get_irq_flags(struct resource *res) -+{ -+ unsigned int flags = IRQF_DISABLED | IRQF_SHARED; -+ -+ flags |= res->flags & IRQF_TRIGGER_MASK; -+ -+ return flags; -+} -+ -+static struct device *dev; -+static struct pda_power_pdata *pdata; -+static struct resource *ac_irq, *usb_irq; -+static struct timer_list charger_timer; -+static struct timer_list supply_timer; -+ -+static int pda_power_get_property(struct power_supply *psy, -+ enum power_supply_property psp, -+ union power_supply_propval *val) -+{ -+ switch (psp) { -+ case POWER_SUPPLY_PROP_ONLINE: -+ if (psy->type == POWER_SUPPLY_TYPE_MAINS) -+ val->intval = pdata->is_ac_online ? -+ pdata->is_ac_online() : 0; -+ else -+ val->intval = pdata->is_usb_online ? -+ pdata->is_usb_online() : 0; -+ break; -+ default: -+ return -EINVAL; -+ } -+ return 0; -+} -+ -+static enum power_supply_property pda_power_props[] = { -+ POWER_SUPPLY_PROP_ONLINE, -+}; -+ -+static char *pda_power_supplied_to[] = { -+ "main-battery", -+ "backup-battery", -+}; -+ -+static struct power_supply pda_power_supplies[] = { -+ { -+ .name = "ac", -+ .type = POWER_SUPPLY_TYPE_MAINS, -+ .supplied_to = pda_power_supplied_to, -+ .num_supplicants = ARRAY_SIZE(pda_power_supplied_to), -+ .properties = pda_power_props, -+ .num_properties = ARRAY_SIZE(pda_power_props), -+ .get_property = pda_power_get_property, -+ }, -+ { -+ .name = "usb", -+ .type = POWER_SUPPLY_TYPE_USB, -+ .supplied_to = pda_power_supplied_to, -+ .num_supplicants = ARRAY_SIZE(pda_power_supplied_to), -+ .properties = pda_power_props, -+ .num_properties = ARRAY_SIZE(pda_power_props), -+ .get_property = pda_power_get_property, -+ }, -+}; -+ -+static void update_charger(void) -+{ -+ if (!pdata->set_charge) -+ return; -+ -+ if (pdata->is_ac_online && pdata->is_ac_online()) { -+ dev_dbg(dev, "charger on (AC)\n"); -+ pdata->set_charge(PDA_POWER_CHARGE_AC); -+ } -+ else if (pdata->is_usb_online && pdata->is_usb_online()) { -+ dev_dbg(dev, "charger on (USB)\n"); -+ pdata->set_charge(PDA_POWER_CHARGE_USB); -+ } -+ else { -+ dev_dbg(dev, "charger off\n"); -+ pdata->set_charge(0); -+ } -+ -+ return; -+} -+ -+static void supply_timer_func(unsigned long irq) -+{ -+ if (ac_irq && irq == ac_irq->start) -+ power_supply_changed(&pda_power_supplies[0]); -+ else if (usb_irq && irq == usb_irq->start) -+ power_supply_changed(&pda_power_supplies[1]); -+ return; -+} -+ -+static void charger_timer_func(unsigned long irq) -+{ -+ update_charger(); -+ -+ /* Okay, charger set. Now wait a bit before notifying supplicants, -+ * charge power should stabilize. */ -+ supply_timer.data = irq; -+ mod_timer(&supply_timer, -+ jiffies + msecs_to_jiffies(pdata->wait_for_charger)); -+ return; -+} -+ -+static irqreturn_t power_changed_isr(int irq, void *unused) -+{ -+ /* Wait a bit before reading ac/usb line status and setting charger, -+ * because ac/usb status readings may lag from irq. */ -+ charger_timer.data = irq; -+ mod_timer(&charger_timer, -+ jiffies + msecs_to_jiffies(pdata->wait_for_status)); -+ return IRQ_HANDLED; -+} -+ -+static int pda_power_probe(struct platform_device *pdev) -+{ -+ int ret = 0; -+ -+ dev = &pdev->dev; -+ -+ if (pdev->id != -1) { -+ dev_err(dev, "it's meaningless to register several " -+ "pda_powers, use id = -1\n"); -+ ret = -EINVAL; -+ goto wrongid; -+ } -+ -+ pdata = pdev->dev.platform_data; -+ -+ update_charger(); -+ -+ if (!pdata->wait_for_status) -+ pdata->wait_for_status = 500; -+ -+ if (!pdata->wait_for_charger) -+ pdata->wait_for_charger = 500; -+ -+ setup_timer(&charger_timer, charger_timer_func, 0); -+ setup_timer(&supply_timer, supply_timer_func, 0); -+ -+ ac_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "ac"); -+ usb_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "usb"); -+ if (!ac_irq && !usb_irq) { -+ dev_err(dev, "no ac/usb irq specified\n"); -+ ret = -ENODEV; -+ goto noirqs; -+ } -+ -+ if (pdata->supplied_to) { -+ pda_power_supplies[0].supplied_to = pdata->supplied_to; -+ pda_power_supplies[1].supplied_to = pdata->supplied_to; -+ pda_power_supplies[0].num_supplicants = pdata->num_supplicants; -+ pda_power_supplies[1].num_supplicants = pdata->num_supplicants; -+ } -+ -+ ret = power_supply_register(&pdev->dev, &pda_power_supplies[0]); -+ if (ret) { -+ dev_err(dev, "failed to register %s power supply\n", -+ pda_power_supplies[0].name); -+ goto supply0_failed; -+ } -+ -+ ret = power_supply_register(&pdev->dev, &pda_power_supplies[1]); -+ if (ret) { -+ dev_err(dev, "failed to register %s power supply\n", -+ pda_power_supplies[1].name); -+ goto supply1_failed; -+ } -+ -+ if (ac_irq) { -+ ret = request_irq(ac_irq->start, power_changed_isr, -+ get_irq_flags(ac_irq), ac_irq->name, -+ &pda_power_supplies[0]); -+ if (ret) { -+ dev_err(dev, "request ac irq failed\n"); -+ goto ac_irq_failed; -+ } -+ } -+ -+ if (usb_irq) { -+ ret = request_irq(usb_irq->start, power_changed_isr, -+ get_irq_flags(usb_irq), usb_irq->name, -+ &pda_power_supplies[1]); -+ if (ret) { -+ dev_err(dev, "request usb irq failed\n"); -+ goto usb_irq_failed; -+ } -+ } -+ -+ goto success; -+ -+usb_irq_failed: -+ if (ac_irq) -+ free_irq(ac_irq->start, &pda_power_supplies[0]); -+ac_irq_failed: -+ power_supply_unregister(&pda_power_supplies[1]); -+supply1_failed: -+ power_supply_unregister(&pda_power_supplies[0]); -+supply0_failed: -+noirqs: -+wrongid: -+success: -+ return ret; -+} -+ -+static int pda_power_remove(struct platform_device *pdev) -+{ -+ if (usb_irq) -+ free_irq(usb_irq->start, &pda_power_supplies[1]); -+ if (ac_irq) -+ free_irq(ac_irq->start, &pda_power_supplies[0]); -+ del_timer_sync(&charger_timer); -+ del_timer_sync(&supply_timer); -+ power_supply_unregister(&pda_power_supplies[1]); -+ power_supply_unregister(&pda_power_supplies[0]); -+ return 0; -+} -+ -+static struct platform_driver pda_power_pdrv = { -+ .driver = { -+ .name = "pda-power", -+ }, -+ .probe = pda_power_probe, -+ .remove = pda_power_remove, -+}; -+ -+static int __init pda_power_init(void) -+{ -+ return platform_driver_register(&pda_power_pdrv); -+} -+ -+static void __exit pda_power_exit(void) -+{ -+ platform_driver_unregister(&pda_power_pdrv); -+ return; -+} -+ -+module_init(pda_power_init); -+module_exit(pda_power_exit); -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Anton Vorontsov <cbou@mail.ru>"); -Index: linux-2.6.22/drivers/power/pmu_battery.c -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/drivers/power/pmu_battery.c 2007-08-23 12:13:52.000000000 +0200 -@@ -0,0 +1,215 @@ -+/* -+ * Battery class driver for Apple PMU -+ * -+ * Copyright © 2006 David Woodhouse <dwmw2@infradead.org> -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include <linux/module.h> -+#include <linux/platform_device.h> -+#include <linux/err.h> -+#include <linux/power_supply.h> -+#include <linux/adb.h> -+#include <linux/pmu.h> -+ -+static struct pmu_battery_dev { -+ struct power_supply bat; -+ struct pmu_battery_info *pbi; -+ char name[16]; -+ int propval; -+} *pbats[PMU_MAX_BATTERIES]; -+ -+#define to_pmu_battery_dev(x) container_of(x, struct pmu_battery_dev, bat) -+ -+/********************************************************************* -+ * Power -+ *********************************************************************/ -+ -+static int pmu_get_ac_prop(struct power_supply *psy, -+ enum power_supply_property psp, -+ union power_supply_propval *val) -+{ -+ switch (psp) { -+ case POWER_SUPPLY_PROP_ONLINE: -+ val->intval = (!!(pmu_power_flags & PMU_PWR_AC_PRESENT)) || -+ (pmu_battery_count == 0); -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static enum power_supply_property pmu_ac_props[] = { -+ POWER_SUPPLY_PROP_ONLINE, -+}; -+ -+static struct power_supply pmu_ac = { -+ .name = "pmu-ac", -+ .type = POWER_SUPPLY_TYPE_MAINS, -+ .properties = pmu_ac_props, -+ .num_properties = ARRAY_SIZE(pmu_ac_props), -+ .get_property = pmu_get_ac_prop, -+}; -+ -+/********************************************************************* -+ * Battery properties -+ *********************************************************************/ -+ -+static char *pmu_batt_types[] = { -+ "Smart", "Comet", "Hooper", "Unknown" -+}; -+ -+static char *pmu_bat_get_model_name(struct pmu_battery_info *pbi) -+{ -+ switch (pbi->flags & PMU_BATT_TYPE_MASK) { -+ case PMU_BATT_TYPE_SMART: -+ return pmu_batt_types[0]; -+ case PMU_BATT_TYPE_COMET: -+ return pmu_batt_types[1]; -+ case PMU_BATT_TYPE_HOOPER: -+ return pmu_batt_types[2]; -+ default: break; -+ } -+ return pmu_batt_types[3]; -+} -+ -+static int pmu_bat_get_property(struct power_supply *psy, -+ enum power_supply_property psp, -+ union power_supply_propval *val) -+{ -+ struct pmu_battery_dev *pbat = to_pmu_battery_dev(psy); -+ struct pmu_battery_info *pbi = pbat->pbi; -+ -+ switch (psp) { -+ case POWER_SUPPLY_PROP_STATUS: -+ if (pbi->flags & PMU_BATT_CHARGING) -+ val->intval = POWER_SUPPLY_STATUS_CHARGING; -+ else -+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING; -+ break; -+ case POWER_SUPPLY_PROP_PRESENT: -+ val->intval = !!(pbi->flags & PMU_BATT_PRESENT); -+ break; -+ case POWER_SUPPLY_PROP_MODEL_NAME: -+ val->strval = pmu_bat_get_model_name(pbi); -+ break; -+ case POWER_SUPPLY_PROP_ENERGY_AVG: -+ val->intval = pbi->charge * 1000; /* mWh -> µWh */ -+ break; -+ case POWER_SUPPLY_PROP_ENERGY_FULL: -+ val->intval = pbi->max_charge * 1000; /* mWh -> µWh */ -+ break; -+ case POWER_SUPPLY_PROP_CURRENT_AVG: -+ val->intval = pbi->amperage * 1000; /* mA -> µA */ -+ break; -+ case POWER_SUPPLY_PROP_VOLTAGE_AVG: -+ val->intval = pbi->voltage * 1000; /* mV -> µV */ -+ break; -+ case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: -+ val->intval = pbi->time_remaining; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static enum power_supply_property pmu_bat_props[] = { -+ POWER_SUPPLY_PROP_STATUS, -+ POWER_SUPPLY_PROP_PRESENT, -+ POWER_SUPPLY_PROP_MODEL_NAME, -+ POWER_SUPPLY_PROP_ENERGY_AVG, -+ POWER_SUPPLY_PROP_ENERGY_FULL, -+ POWER_SUPPLY_PROP_CURRENT_AVG, -+ POWER_SUPPLY_PROP_VOLTAGE_AVG, -+ POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, -+}; -+ -+/********************************************************************* -+ * Initialisation -+ *********************************************************************/ -+ -+static struct platform_device *bat_pdev; -+ -+static int __init pmu_bat_init(void) -+{ -+ int ret; -+ int i; -+ -+ bat_pdev = platform_device_register_simple("pmu-battery", -+ 0, NULL, 0); -+ if (IS_ERR(bat_pdev)) { -+ ret = PTR_ERR(bat_pdev); -+ goto pdev_register_failed; -+ } -+ -+ ret = power_supply_register(&bat_pdev->dev, &pmu_ac); -+ if (ret) -+ goto ac_register_failed; -+ -+ for (i = 0; i < pmu_battery_count; i++) { -+ struct pmu_battery_dev *pbat = kzalloc(sizeof(*pbat), -+ GFP_KERNEL); -+ if (!pbat) -+ break; -+ -+ sprintf(pbat->name, "PMU battery %d", i); -+ pbat->bat.name = pbat->name; -+ pbat->bat.properties = pmu_bat_props; -+ pbat->bat.num_properties = ARRAY_SIZE(pmu_bat_props); -+ pbat->bat.get_property = pmu_bat_get_property; -+ pbat->pbi = &pmu_batteries[i]; -+ -+ ret = power_supply_register(&bat_pdev->dev, &pbat->bat); -+ if (ret) { -+ kfree(pbat); -+ goto battery_register_failed; -+ } -+ pbats[i] = pbat; -+ } -+ -+ goto success; -+ -+battery_register_failed: -+ while (i--) { -+ if (!pbats[i]) -+ continue; -+ power_supply_unregister(&pbats[i]->bat); -+ kfree(pbats[i]); -+ } -+ power_supply_unregister(&pmu_ac); -+ac_register_failed: -+ platform_device_unregister(bat_pdev); -+pdev_register_failed: -+success: -+ return ret; -+} -+ -+static void __exit pmu_bat_exit(void) -+{ -+ int i; -+ -+ for (i = 0; i < PMU_MAX_BATTERIES; i++) { -+ if (!pbats[i]) -+ continue; -+ power_supply_unregister(&pbats[i]->bat); -+ kfree(pbats[i]); -+ } -+ power_supply_unregister(&pmu_ac); -+ platform_device_unregister(bat_pdev); -+ -+ return; -+} -+ -+module_init(pmu_bat_init); -+module_exit(pmu_bat_exit); -+ -+MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("PMU battery driver"); -Index: linux-2.6.22/drivers/power/power_supply_core.c -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/drivers/power/power_supply_core.c 2007-08-23 12:13:52.000000000 +0200 -@@ -0,0 +1,168 @@ -+/* -+ * Universal power supply monitor class -+ * -+ * Copyright (c) 2007 Anton Vorontsov <cbou@mail.ru> -+ * Copyright (c) 2004 Szabolcs Gyurko -+ * Copyright (c) 2003 Ian Molton <spyro@f2s.com> -+ * -+ * Modified: 2004, Oct Szabolcs Gyurko -+ * -+ * You may use this code as per GPL version 2 -+ */ -+ -+#include <linux/module.h> -+#include <linux/types.h> -+#include <linux/init.h> -+#include <linux/device.h> -+#include <linux/err.h> -+#include <linux/power_supply.h> -+#include "power_supply.h" -+ -+struct class *power_supply_class; -+ -+static void power_supply_changed_work(struct work_struct *work) -+{ -+ struct power_supply *psy = container_of(work, struct power_supply, -+ changed_work); -+ int i; -+ -+ dev_dbg(psy->dev, "%s\n", __FUNCTION__); -+ -+ for (i = 0; i < psy->num_supplicants; i++) { -+ struct device *dev; -+ -+ down(&power_supply_class->sem); -+ list_for_each_entry(dev, &power_supply_class->devices, node) { -+ struct power_supply *pst = dev_get_drvdata(dev); -+ -+ if (!strcmp(psy->supplied_to[i], pst->name)) { -+ if (pst->external_power_changed) -+ pst->external_power_changed(pst); -+ } -+ } -+ up(&power_supply_class->sem); -+ } -+ -+ power_supply_update_leds(psy); -+ -+ kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE); -+ -+ return; -+} -+ -+void power_supply_changed(struct power_supply *psy) -+{ -+ dev_dbg(psy->dev, "%s\n", __FUNCTION__); -+ -+ schedule_work(&psy->changed_work); -+ -+ return; -+} -+ -+int power_supply_am_i_supplied(struct power_supply *psy) -+{ -+ union power_supply_propval ret = {0,}; -+ struct device *dev; -+ -+ down(&power_supply_class->sem); -+ list_for_each_entry(dev, &power_supply_class->devices, node) { -+ struct power_supply *epsy = dev_get_drvdata(dev); -+ int i; -+ -+ for (i = 0; i < epsy->num_supplicants; i++) { -+ if (!strcmp(epsy->supplied_to[i], psy->name)) { -+ if (epsy->get_property(epsy, -+ POWER_SUPPLY_PROP_ONLINE, &ret)) -+ continue; -+ if (ret.intval) -+ goto out; -+ } -+ } -+ } -+out: -+ up(&power_supply_class->sem); -+ -+ dev_dbg(psy->dev, "%s %d\n", __FUNCTION__, ret.intval); -+ -+ return ret.intval; -+} -+ -+int power_supply_register(struct device *parent, struct power_supply *psy) -+{ -+ int rc = 0; -+ -+ psy->dev = device_create(power_supply_class, parent, 0, -+ "%s", psy->name); -+ if (IS_ERR(psy->dev)) { -+ rc = PTR_ERR(psy->dev); -+ goto dev_create_failed; -+ } -+ -+ dev_set_drvdata(psy->dev, psy); -+ -+ INIT_WORK(&psy->changed_work, power_supply_changed_work); -+ -+ rc = power_supply_create_attrs(psy); -+ if (rc) -+ goto create_attrs_failed; -+ -+ rc = power_supply_create_triggers(psy); -+ if (rc) -+ goto create_triggers_failed; -+ -+ power_supply_changed(psy); -+ -+ goto success; -+ -+create_triggers_failed: -+ power_supply_remove_attrs(psy); -+create_attrs_failed: -+ device_unregister(psy->dev); -+dev_create_failed: -+success: -+ return rc; -+} -+ -+void power_supply_unregister(struct power_supply *psy) -+{ -+ flush_scheduled_work(); -+ power_supply_remove_triggers(psy); -+ power_supply_remove_attrs(psy); -+ device_unregister(psy->dev); -+ return; -+} -+ -+static int __init power_supply_class_init(void) -+{ -+ power_supply_class = class_create(THIS_MODULE, "power_supply"); -+ -+ if (IS_ERR(power_supply_class)) -+ return PTR_ERR(power_supply_class); -+ -+ power_supply_class->dev_uevent = power_supply_uevent; -+ -+ return 0; -+} -+ -+static void __exit power_supply_class_exit(void) -+{ -+ class_destroy(power_supply_class); -+ return; -+} -+ -+EXPORT_SYMBOL_GPL(power_supply_changed); -+EXPORT_SYMBOL_GPL(power_supply_am_i_supplied); -+EXPORT_SYMBOL_GPL(power_supply_register); -+EXPORT_SYMBOL_GPL(power_supply_unregister); -+ -+/* exported for the APM Power driver, APM emulation */ -+EXPORT_SYMBOL_GPL(power_supply_class); -+ -+subsys_initcall(power_supply_class_init); -+module_exit(power_supply_class_exit); -+ -+MODULE_DESCRIPTION("Universal power supply monitor class"); -+MODULE_AUTHOR("Ian Molton <spyro@f2s.com>, " -+ "Szabolcs Gyurko, " -+ "Anton Vorontsov <cbou@mail.ru>"); -+MODULE_LICENSE("GPL"); -Index: linux-2.6.22/drivers/power/power_supply.h -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/drivers/power/power_supply.h 2007-08-23 12:13:52.000000000 +0200 -@@ -0,0 +1,42 @@ -+/* -+ * Functions private to power supply class -+ * -+ * Copyright (c) 2007 Anton Vorontsov <cbou@mail.ru> -+ * Copyright (c) 2004 Szabolcs Gyurko -+ * Copyright (c) 2003 Ian Molton <spyro@f2s.com> -+ * -+ * Modified: 2004, Oct Szabolcs Gyurko -+ * -+ * You may use this code as per GPL version 2 -+ */ -+ -+#ifdef CONFIG_SYSFS -+ -+extern int power_supply_create_attrs(struct power_supply *psy); -+extern void power_supply_remove_attrs(struct power_supply *psy); -+extern int power_supply_uevent(struct device *dev, char **envp, int num_envp, -+ char *buffer, int buffer_size); -+ -+#else -+ -+static inline int power_supply_create_attrs(struct power_supply *psy) -+{ return 0; } -+static inline void power_supply_remove_attrs(struct power_supply *psy) {} -+#define power_supply_uevent NULL -+ -+#endif /* CONFIG_SYSFS */ -+ -+#ifdef CONFIG_LEDS_TRIGGERS -+ -+extern void power_supply_update_leds(struct power_supply *psy); -+extern int power_supply_create_triggers(struct power_supply *psy); -+extern void power_supply_remove_triggers(struct power_supply *psy); -+ -+#else -+ -+static inline void power_supply_update_leds(struct power_supply *psy) {} -+static inline int power_supply_create_triggers(struct power_supply *psy) -+{ return 0; } -+static inline void power_supply_remove_triggers(struct power_supply *psy) {} -+ -+#endif /* CONFIG_LEDS_TRIGGERS */ -Index: linux-2.6.22/drivers/power/power_supply_leds.c -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/drivers/power/power_supply_leds.c 2007-08-23 12:13:52.000000000 +0200 -@@ -0,0 +1,188 @@ -+/* -+ * LEDs triggers for power supply class -+ * -+ * Copyright (c) 2007 Anton Vorontsov <cbou@mail.ru> -+ * Copyright (c) 2004 Szabolcs Gyurko -+ * Copyright (c) 2003 Ian Molton <spyro@f2s.com> -+ * -+ * Modified: 2004, Oct Szabolcs Gyurko -+ * -+ * You may use this code as per GPL version 2 -+ */ -+ -+#include <linux/power_supply.h> -+ -+/* If we have hwtimer trigger, then use it to blink charging LED */ -+ -+#if defined(CONFIG_LEDS_TRIGGER_HWTIMER) || \ -+ (defined(CONFIG_BATTERY_MODULE) && \ -+ defined(CONFIG_LEDS_TRIGGER_HWTIMER_MODULE)) -+ #define led_trigger_register_charging led_trigger_register_hwtimer -+ #define led_trigger_unregister_charging led_trigger_unregister_hwtimer -+#else -+ #define led_trigger_register_charging led_trigger_register_simple -+ #define led_trigger_unregister_charging led_trigger_unregister_simple -+#endif -+ -+/* Battery specific LEDs triggers. */ -+ -+static void power_supply_update_bat_leds(struct power_supply *psy) -+{ -+ union power_supply_propval status; -+ -+ if (psy->get_property(psy, POWER_SUPPLY_PROP_STATUS, &status)) -+ return; -+ -+ dev_dbg(psy->dev, "%s %d\n", __FUNCTION__, status.intval); -+ -+ switch(status.intval) { -+ case POWER_SUPPLY_STATUS_FULL: -+ led_trigger_event(psy->charging_full_trig, LED_FULL); -+ led_trigger_event(psy->charging_trig, LED_OFF); -+ led_trigger_event(psy->full_trig, LED_FULL); -+ break; -+ case POWER_SUPPLY_STATUS_CHARGING: -+ led_trigger_event(psy->charging_full_trig, LED_FULL); -+ led_trigger_event(psy->charging_trig, LED_FULL); -+ led_trigger_event(psy->full_trig, LED_OFF); -+ break; -+ default: -+ led_trigger_event(psy->charging_full_trig, LED_OFF); -+ led_trigger_event(psy->charging_trig, LED_OFF); -+ led_trigger_event(psy->full_trig, LED_OFF); -+ break; -+ } -+ -+ return; -+} -+ -+static int power_supply_create_bat_triggers(struct power_supply *psy) -+{ -+ int rc = 0; -+ -+ psy->charging_full_trig_name = kmalloc(strlen(psy->name) + -+ sizeof("-charging-or-full"), GFP_KERNEL); -+ if (!psy->charging_full_trig_name) -+ goto charging_full_failed; -+ -+ psy->charging_trig_name = kmalloc(strlen(psy->name) + -+ sizeof("-charging"), GFP_KERNEL); -+ if (!psy->charging_trig_name) -+ goto charging_failed; -+ -+ psy->full_trig_name = kmalloc(strlen(psy->name) + -+ sizeof("-full"), GFP_KERNEL); -+ if (!psy->full_trig_name) -+ goto full_failed; -+ -+ strcpy(psy->charging_full_trig_name, psy->name); -+ strcat(psy->charging_full_trig_name, "-charging-or-full"); -+ strcpy(psy->charging_trig_name, psy->name); -+ strcat(psy->charging_trig_name, "-charging"); -+ strcpy(psy->full_trig_name, psy->name); -+ strcat(psy->full_trig_name, "-full"); -+ -+ led_trigger_register_simple(psy->charging_full_trig_name, -+ &psy->charging_full_trig); -+ led_trigger_register_charging(psy->charging_trig_name, -+ &psy->charging_trig); -+ led_trigger_register_simple(psy->full_trig_name, -+ &psy->full_trig); -+ -+ goto success; -+ -+full_failed: -+ kfree(psy->charging_trig_name); -+charging_failed: -+ kfree(psy->charging_full_trig_name); -+charging_full_failed: -+ rc = -ENOMEM; -+success: -+ return rc; -+} -+ -+static void power_supply_remove_bat_triggers(struct power_supply *psy) -+{ -+ led_trigger_unregister_simple(psy->charging_full_trig); -+ led_trigger_unregister_charging(psy->charging_trig); -+ led_trigger_unregister_simple(psy->full_trig); -+ kfree(psy->full_trig_name); -+ kfree(psy->charging_trig_name); -+ kfree(psy->charging_full_trig_name); -+ return; -+} -+ -+/* Generated power specific LEDs triggers. */ -+ -+static void power_supply_update_gen_leds(struct power_supply *psy) -+{ -+ union power_supply_propval online; -+ -+ if (psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &online)) -+ return; -+ -+ dev_dbg(psy->dev, "%s %d\n", __FUNCTION__, online.intval); -+ -+ if (online.intval) -+ led_trigger_event(psy->online_trig, LED_FULL); -+ else -+ led_trigger_event(psy->online_trig, LED_OFF); -+ -+ return; -+} -+ -+static int power_supply_create_gen_triggers(struct power_supply *psy) -+{ -+ int rc = 0; -+ -+ psy->online_trig_name = kmalloc(strlen(psy->name) + sizeof("-online"), -+ GFP_KERNEL); -+ if (!psy->online_trig_name) -+ goto online_failed; -+ -+ strcpy(psy->online_trig_name, psy->name); -+ strcat(psy->online_trig_name, "-online"); -+ -+ led_trigger_register_simple(psy->online_trig_name, &psy->online_trig); -+ -+ goto success; -+ -+online_failed: -+ rc = -ENOMEM; -+success: -+ return rc; -+} -+ -+static void power_supply_remove_gen_triggers(struct power_supply *psy) -+{ -+ led_trigger_unregister_simple(psy->online_trig); -+ kfree(psy->online_trig_name); -+ return; -+} -+ -+/* Choice what triggers to create&update. */ -+ -+void power_supply_update_leds(struct power_supply *psy) -+{ -+ if (psy->type == POWER_SUPPLY_TYPE_BATTERY) -+ power_supply_update_bat_leds(psy); -+ else -+ power_supply_update_gen_leds(psy); -+ return; -+} -+ -+int power_supply_create_triggers(struct power_supply *psy) -+{ -+ if (psy->type == POWER_SUPPLY_TYPE_BATTERY) -+ return power_supply_create_bat_triggers(psy); -+ return power_supply_create_gen_triggers(psy); -+} -+ -+void power_supply_remove_triggers(struct power_supply *psy) -+{ -+ if (psy->type == POWER_SUPPLY_TYPE_BATTERY) -+ power_supply_remove_bat_triggers(psy); -+ else -+ power_supply_remove_gen_triggers(psy); -+ return; -+} -Index: linux-2.6.22/drivers/power/power_supply_sysfs.c -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/drivers/power/power_supply_sysfs.c 2007-08-23 12:13:52.000000000 +0200 -@@ -0,0 +1,289 @@ -+/* -+ * Sysfs interface for the universal power supply monitor class -+ * -+ * Copyright © 2007 David Woodhouse <dwmw2@infradead.org> -+ * Copyright (c) 2007 Anton Vorontsov <cbou@mail.ru> -+ * Copyright (c) 2004 Szabolcs Gyurko -+ * Copyright (c) 2003 Ian Molton <spyro@f2s.com> -+ * -+ * Modified: 2004, Oct Szabolcs Gyurko -+ * -+ * You may use this code as per GPL version 2 -+ */ -+ -+#include <linux/ctype.h> -+#include <linux/power_supply.h> -+ -+/* -+ * This is because the name "current" breaks the device attr macro. -+ * The "current" word resolvs to "(get_current())" so instead of -+ * "current" "(get_current())" appears in the sysfs. -+ * -+ * The source of this definition is the device.h which calls __ATTR -+ * macro in sysfs.h which calls the __stringify macro. -+ * -+ * Only modification that the name is not tried to be resolved -+ * (as a macro let's say). -+ */ -+ -+#define POWER_SUPPLY_ATTR(_name) \ -+{ \ -+ .attr = { .name = #_name, .mode = 0444, .owner = THIS_MODULE }, \ -+ .show = power_supply_show_property, \ -+ .store = NULL, \ -+} -+ -+static struct device_attribute power_supply_attrs[]; -+ -+static ssize_t power_supply_show_property(struct device *dev, -+ struct device_attribute *attr, -+ char *buf) { -+ static char *status_text[] = { -+ "Unknown", "Charging", "Discharging", "Not charging", "Full" -+ }; -+ static char *health_text[] = { -+ "Unknown", "Good", "Overheat", "Dead" -+ }; -+ static char *technology_text[] = { -+ "Unknown", "NiMH", "Li-ion", "Li-poly" -+ }; -+ static char *capacity_level_text[] = { -+ "Unknown", "Critical", "Low", "Normal", "High", "Full" -+ }; -+ ssize_t ret; -+ struct power_supply *psy = dev_get_drvdata(dev); -+ const ptrdiff_t off = attr - power_supply_attrs; -+ union power_supply_propval value; -+ -+ ret = psy->get_property(psy, off, &value); -+ -+ if (ret < 0) { -+ dev_err(dev, "driver failed to report `%s' property\n", -+ attr->attr.name); -+ return ret; -+ } -+ -+ if (off == POWER_SUPPLY_PROP_STATUS) -+ return sprintf(buf, "%s\n", status_text[value.intval]); -+ else if (off == POWER_SUPPLY_PROP_HEALTH) -+ return sprintf(buf, "%s\n", health_text[value.intval]); -+ else if (off == POWER_SUPPLY_PROP_TECHNOLOGY) -+ return sprintf(buf, "%s\n", technology_text[value.intval]); -+ else if (off == POWER_SUPPLY_PROP_CAPACITY_LEVEL) -+ return sprintf(buf, "%s\n", -+ capacity_level_text[value.intval]); -+ else if (off == POWER_SUPPLY_PROP_MODEL_NAME) -+ return sprintf(buf, "%s\n", value.strval); -+ -+ return sprintf(buf, "%d\n", value.intval); -+} -+ -+/* Must be in the same order as POWER_SUPPLY_PROP_* */ -+static struct device_attribute power_supply_attrs[] = { -+ /* Properties of type `int' */ -+ POWER_SUPPLY_ATTR(status), -+ POWER_SUPPLY_ATTR(health), -+ POWER_SUPPLY_ATTR(present), -+ POWER_SUPPLY_ATTR(online), -+ POWER_SUPPLY_ATTR(technology), -+ POWER_SUPPLY_ATTR(voltage_max_design), -+ POWER_SUPPLY_ATTR(voltage_min_design), -+ POWER_SUPPLY_ATTR(voltage_now), -+ POWER_SUPPLY_ATTR(voltage_avg), -+ POWER_SUPPLY_ATTR(current_now), -+ POWER_SUPPLY_ATTR(current_avg), -+ POWER_SUPPLY_ATTR(charge_full_design), -+ POWER_SUPPLY_ATTR(charge_empty_design), -+ POWER_SUPPLY_ATTR(charge_full), -+ POWER_SUPPLY_ATTR(charge_empty), -+ POWER_SUPPLY_ATTR(charge_now), -+ POWER_SUPPLY_ATTR(charge_avg), -+ POWER_SUPPLY_ATTR(energy_full_design), -+ POWER_SUPPLY_ATTR(energy_empty_design), -+ POWER_SUPPLY_ATTR(energy_full), -+ POWER_SUPPLY_ATTR(energy_empty), -+ POWER_SUPPLY_ATTR(energy_now), -+ POWER_SUPPLY_ATTR(energy_avg), -+ POWER_SUPPLY_ATTR(capacity), -+ POWER_SUPPLY_ATTR(capacity_level), -+ POWER_SUPPLY_ATTR(temp), -+ POWER_SUPPLY_ATTR(temp_ambient), -+ POWER_SUPPLY_ATTR(time_to_empty_now), -+ POWER_SUPPLY_ATTR(time_to_empty_avg), -+ POWER_SUPPLY_ATTR(time_to_full_now), -+ POWER_SUPPLY_ATTR(time_to_full_avg), -+ /* Properties of type `const char *' */ -+ POWER_SUPPLY_ATTR(model_name), -+}; -+ -+static ssize_t power_supply_show_static_attrs(struct device *dev, -+ struct device_attribute *attr, -+ char *buf) { -+ static char *type_text[] = { "Battery", "UPS", "Mains", "USB" }; -+ struct power_supply *psy = dev_get_drvdata(dev); -+ -+ return sprintf(buf, "%s\n", type_text[psy->type]); -+} -+ -+static struct device_attribute power_supply_static_attrs[] = { -+ __ATTR(type, 0444, power_supply_show_static_attrs, NULL), -+}; -+ -+int power_supply_create_attrs(struct power_supply *psy) -+{ -+ int rc = 0; -+ int i, j; -+ -+ for (i = 0; i < ARRAY_SIZE(power_supply_static_attrs); i++) { -+ rc = device_create_file(psy->dev, -+ &power_supply_static_attrs[i]); -+ if (rc) -+ goto statics_failed; -+ } -+ -+ for (j = 0; j < psy->num_properties; j++) { -+ rc = device_create_file(psy->dev, -+ &power_supply_attrs[psy->properties[j]]); -+ if (rc) -+ goto dynamics_failed; -+ } -+ -+ goto succeed; -+ -+dynamics_failed: -+ while (j--) -+ device_remove_file(psy->dev, -+ &power_supply_attrs[psy->properties[j]]); -+statics_failed: -+ while (i--) -+ device_remove_file(psy->dev, -+ &power_supply_static_attrs[psy->properties[i]]); -+succeed: -+ return rc; -+} -+ -+void power_supply_remove_attrs(struct power_supply *psy) -+{ -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(power_supply_static_attrs); i++) -+ device_remove_file(psy->dev, -+ &power_supply_static_attrs[i]); -+ -+ for (i = 0; i < psy->num_properties; i++) -+ device_remove_file(psy->dev, -+ &power_supply_attrs[psy->properties[i]]); -+ -+ return; -+} -+ -+static char *kstruprdup(const char *str, gfp_t gfp) -+{ -+ char *ret, *ustr; -+ -+ ustr = ret = kmalloc(strlen(str) + 1, gfp); -+ -+ if (!ret) -+ return NULL; -+ -+ while (*str) -+ *ustr++ = toupper(*str++); -+ -+ *ustr = 0; -+ -+ return ret; -+} -+ -+int power_supply_uevent(struct device *dev, char **envp, int num_envp, -+ char *buffer, int buffer_size) -+{ -+ struct power_supply *psy = dev_get_drvdata(dev); -+ int i = 0, length = 0, ret = 0, j; -+ char *prop_buf; -+ char *attrname; -+ -+ dev_dbg(dev, "uevent\n"); -+ -+ if (!psy) { -+ dev_dbg(dev, "No power supply yet\n"); -+ return ret; -+ } -+ -+ dev_dbg(dev, "POWER_SUPPLY_NAME=%s\n", psy->name); -+ -+ ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, -+ &length, "POWER_SUPPLY_NAME=%s", psy->name); -+ if (ret) -+ return ret; -+ -+ prop_buf = (char *)get_zeroed_page(GFP_KERNEL); -+ if (!prop_buf) -+ return -ENOMEM; -+ -+ for (j = 0; j < ARRAY_SIZE(power_supply_static_attrs); j++) { -+ struct device_attribute *attr; -+ char *line; -+ -+ attr = &power_supply_static_attrs[j]; -+ -+ ret = power_supply_show_static_attrs(dev, attr, prop_buf); -+ if (ret < 0) -+ goto out; -+ -+ line = strchr(prop_buf, '\n'); -+ if (line) -+ *line = 0; -+ -+ attrname = kstruprdup(attr->attr.name, GFP_KERNEL); -+ if (!attrname) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ -+ dev_dbg(dev, "Static prop %s=%s\n", attrname, prop_buf); -+ -+ ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, -+ &length, "POWER_SUPPLY_%s=%s", -+ attrname, prop_buf); -+ kfree(attrname); -+ if (ret) -+ goto out; -+ } -+ -+ dev_dbg(dev, "%zd dynamic props\n", psy->num_properties); -+ -+ for (j = 0; j < psy->num_properties; j++) { -+ struct device_attribute *attr; -+ char *line; -+ -+ attr = &power_supply_attrs[psy->properties[j]]; -+ -+ ret = power_supply_show_property(dev, attr, prop_buf); -+ if (ret < 0) -+ goto out; -+ -+ line = strchr(prop_buf, '\n'); -+ if (line) -+ *line = 0; -+ -+ attrname = kstruprdup(attr->attr.name, GFP_KERNEL); -+ if (!attrname) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ -+ dev_dbg(dev, "prop %s=%s\n", attrname, prop_buf); -+ -+ ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, -+ &length, "POWER_SUPPLY_%s=%s", -+ attrname, prop_buf); -+ kfree(attrname); -+ if (ret) -+ goto out; -+ } -+ -+out: -+ free_page((unsigned long)prop_buf); -+ -+ return ret; -+} -Index: linux-2.6.22/drivers/power/simpad-battery.c -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/drivers/power/simpad-battery.c 2007-08-23 12:13:52.000000000 +0200 -@@ -0,0 +1,242 @@ -+/* -+ * linux/drivers/misc/simpad-battery.c -+ * -+ * Copyright (C) 2005 Holger Hans Peter Freyther -+ * Copyright (C) 2001 Juergen Messerer -+ * -+ * 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 -+ * the Free Software Foundation; either version 2 of the License. -+ * -+ * Read the Battery Level through the UCB1x00 chip. T-Sinuspad is -+ * unsupported for now. -+ * -+ */ -+ -+#include <linux/battery.h> -+#include <asm/dma.h> -+#include "ucb1x00.h" -+ -+ -+/* -+ * Conversion from AD -> mV -+ * 7.5V = 1023 7.3313mV/Digit -+ * -+ * 400 Units == 9.7V -+ * a = ADC value -+ * 21 = ADC error -+ * 12600 = Divident to get 2*7.3242 -+ * 860 = Divider to get 2*7.3242 -+ * 170 = Voltagedrop over -+ */ -+#define CALIBRATE_BATTERY(a) ((((a + 21)*12600)/860) + 170) -+ -+/* -+ * We have two types of batteries a small and a large one -+ * To get the right value we to distinguish between those two -+ * 450 Units == 15 V -+ */ -+#define CALIBRATE_SUPPLY(a) (((a) * 1500) / 45) -+#define MIN_SUPPLY 12000 /* Less then 12V means no powersupply */ -+ -+/* -+ * Charging Current -+ * if value is >= 50 then charging is on -+ */ -+#define CALIBRATE_CHARGING(a) (((a)* 1000)/(152/4))) -+ -+struct simpad_battery_t { -+ struct battery battery; -+ struct ucb1x00* ucb; -+ -+ /* -+ * Variables for the values to one time support -+ * T-Sinuspad as well -+ */ -+ int min_voltage; -+ int min_current; -+ int min_charge; -+ -+ int max_voltage; -+ int max_current; -+ int max_charge; -+ -+ int min_supply; -+ int charging_led_label; -+ int charging_max_label; -+ int batt_full; -+ int batt_low; -+ int batt_critical; -+ int batt_empty; -+}; -+ -+static int simpad_get_min_voltage(struct battery* _battery ) -+{ -+ struct simpad_battery_t* battery = (struct simpad_battery_t*)_battery; -+ return battery->min_voltage; -+} -+ -+static int simpad_get_min_current(struct battery* _battery) -+{ -+ struct simpad_battery_t* battery = (struct simpad_battery_t*)_battery; -+ return battery->min_current; -+} -+ -+static int simpad_get_min_charge(struct battery* _battery) -+{ -+ struct simpad_battery_t* battery = (struct simpad_battery_t*)_battery; -+ return battery->min_charge; -+} -+ -+static int simpad_get_max_voltage(struct battery* _battery) -+{ -+ struct simpad_battery_t* battery = (struct simpad_battery_t*)_battery; -+ return battery->max_voltage; -+} -+ -+static int simpad_get_max_current(struct battery* _battery) -+{ -+ struct simpad_battery_t* battery = (struct simpad_battery_t*)_battery; -+ return battery->max_current; -+} -+ -+static int simpad_get_max_charge(struct battery* _battery) -+{ -+ struct simpad_battery_t* battery = (struct simpad_battery_t*)_battery; -+ return battery->max_charge; -+} -+ -+static int simpad_get_temp(struct battery* _battery) -+{ -+ return 0; -+} -+ -+static int simpad_get_voltage(struct battery* _battery) -+{ -+ int val; -+ struct simpad_battery_t* battery = (struct simpad_battery_t*)_battery; -+ -+ -+ ucb1x00_adc_enable(battery->ucb); -+ val = ucb1x00_adc_read(battery->ucb, UCB_ADC_INP_AD1, UCB_NOSYNC); -+ ucb1x00_adc_disable(battery->ucb); -+ -+ return CALIBRATE_BATTERY(val); -+} -+ -+static int simpad_get_current(struct battery* _battery) -+{ -+ int val; -+ struct simpad_battery_t* battery = (struct simpad_battery_t*)_battery; -+ -+ ucb1x00_adc_enable(battery->ucb); -+ val = ucb1x00_adc_read(battery->ucb, UCB_ADC_INP_AD3, UCB_NOSYNC); -+ ucb1x00_adc_disable(battery->ucb); -+ -+ return val; -+} -+ -+static int simpad_get_charge(struct battery* _battery) -+{ -+ int val; -+ struct simpad_battery_t* battery = (struct simpad_battery_t*)_battery; -+ -+ ucb1x00_adc_enable(battery->ucb); -+ val = ucb1x00_adc_read(battery->ucb, UCB_ADC_INP_AD2, UCB_NOSYNC); -+ ucb1x00_adc_disable(battery->ucb); -+ -+ return CALIBRATE_SUPPLY(val); -+ -+} -+ -+static int simpad_get_status(struct battery* _battery) -+{ -+ struct simpad_battery_t* battery = (struct simpad_battery_t*)(_battery); -+ int vcharger = simpad_get_voltage(_battery); -+ int icharger = simpad_get_current(_battery); -+ -+ int status = BATTERY_STATUS_UNKNOWN; -+ if(icharger > battery->charging_led_label) -+ status = BATTERY_STATUS_CHARGING; -+ else if(vcharger > battery->min_supply) -+ status = BATTERY_STATUS_NOT_CHARGING; -+ else -+ status = BATTERY_STATUS_DISCHARGING; -+ -+ return status; -+} -+ -+static struct simpad_battery_t simpad_battery = { -+ .battery = { -+ .get_min_voltage = simpad_get_min_voltage, -+ .get_min_current = simpad_get_min_current, -+ .get_min_charge = simpad_get_min_charge, -+ .get_max_voltage = simpad_get_max_voltage, -+ .get_max_current = simpad_get_max_current, -+ .get_max_charge = simpad_get_max_charge, -+ .get_temp = simpad_get_temp, -+ .get_voltage = simpad_get_voltage, -+ .get_current = simpad_get_current, -+ .get_charge = simpad_get_charge, -+ .get_status = simpad_get_status, -+ }, -+ .min_voltage = 0, -+ .min_current = 0, -+ .min_charge = 0, -+ .max_voltage = 0, -+ .max_current = 0, -+ .max_charge = 0, -+ -+ .min_supply = 1200, -+ .charging_led_label = 18, -+ .charging_max_label = 265, -+ .batt_full = 8300, -+ .batt_low = 7300, -+ .batt_critical = 6800, -+ .batt_empty = 6500, -+}; -+ -+ -+ -+/* -+ * UCB glue code -+ */ -+static int ucb1x00_battery_add(struct class_device *dev) -+{ -+ struct ucb1x00 *ucb = classdev_to_ucb1x00(dev); -+ simpad_battery.ucb = ucb; -+ -+ battery_class_register(&simpad_battery.battery); -+ -+ return 0; -+} -+ -+static void ucb1x00_battery_remove(struct class_device *dev) -+{ -+ return battery_class_unregister(&simpad_battery.battery); -+} -+ -+ -+static struct ucb1x00_class_interface ucb1x00_battery_interface = { -+ .interface = { -+ .add = ucb1x00_battery_add, -+ .remove = ucb1x00_battery_remove, -+ }, -+}; -+ -+ -+static int __init battery_register(void) -+{ -+ return ucb1x00_register_interface(&ucb1x00_battery_interface); -+} -+ -+static void __exit battery_unregister(void) -+{ -+ ucb1x00_unregister_interface(&ucb1x00_battery_interface); -+} -+ -+module_init(battery_register); -+module_exit(battery_unregister); -+ -+MODULE_AUTHOR("Holger Hans Peter Freyther"); -+MODULE_LICENSE("GPL"); -Index: linux-2.6.22/arch/arm/Kconfig -=================================================================== ---- linux-2.6.22.orig/arch/arm/Kconfig 2007-08-23 12:17:42.000000000 +0200 -+++ linux-2.6.22/arch/arm/Kconfig 2007-08-23 12:22:28.000000000 +0200 -@@ -1016,6 +1016,8 @@ - - source "drivers/w1/Kconfig" - -+source "drivers/power/Kconfig" -+ - source "drivers/hwmon/Kconfig" - - #source "drivers/l3/Kconfig" -Index: linux-2.6.22/drivers/Kconfig -=================================================================== ---- linux-2.6.22.orig/drivers/Kconfig 2007-08-23 12:21:27.000000000 +0200 -+++ linux-2.6.22/drivers/Kconfig 2007-08-23 12:22:03.000000000 +0200 -@@ -54,6 +54,8 @@ - - source "drivers/w1/Kconfig" - -+source "drivers/power/Kconfig" -+ - source "drivers/hwmon/Kconfig" - - source "drivers/mfd/Kconfig" -Index: linux-2.6.22/drivers/Makefile -=================================================================== ---- linux-2.6.22.orig/drivers/Makefile 2007-08-23 12:33:58.000000000 +0200 -+++ linux-2.6.22/drivers/Makefile 2007-08-23 12:34:34.000000000 +0200 -@@ -61,6 +61,7 @@ - obj-$(CONFIG_RTC_LIB) += rtc/ - obj-y += i2c/ - obj-$(CONFIG_W1) += w1/ -+obj-$(CONFIG_POWER_SUPPLY) += power/ - obj-$(CONFIG_HWMON) += hwmon/ - obj-$(CONFIG_PHONE) += telephony/ - obj-$(CONFIG_MD) += md/ -Index: linux-2.6.22/include/linux/power_supply.h -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/include/linux/power_supply.h 2007-08-23 12:37:10.000000000 +0200 -@@ -0,0 +1,175 @@ -+/* -+ * Universal power supply monitor class -+ * -+ * Copyright (c) 2007 Anton Vorontsov <cbou@mail.ru> -+ * Copyright (c) 2004 Szabolcs Gyurko -+ * Copyright (c) 2003 Ian Molton <spyro@f2s.com> -+ * -+ * Modified: 2004, Oct Szabolcs Gyurko -+ * -+ * You may use this code as per GPL version 2 -+ */ -+ -+#ifndef __LINUX_POWER_SUPPLY_H__ -+#define __LINUX_POWER_SUPPLY_H__ -+ -+#include <linux/device.h> -+#include <linux/workqueue.h> -+#include <linux/leds.h> -+ -+/* -+ * All voltages, currents, charges, energies, time and temperatures in uV, -+ * uA, uAh, uWh, seconds and tenths of degree Celsius unless otherwise -+ * stated. It's driver's job to convert its raw values to units in which -+ * this class operates. -+ */ -+ -+/* -+ * For systems where the charger determines the maximum battery capacity -+ * the min and max fields should be used to present these values to user -+ * space. Unused/unknown fields will not appear in sysfs. -+ */ -+ -+enum { -+ POWER_SUPPLY_STATUS_UNKNOWN = 0, -+ POWER_SUPPLY_STATUS_CHARGING, -+ POWER_SUPPLY_STATUS_DISCHARGING, -+ POWER_SUPPLY_STATUS_NOT_CHARGING, -+ POWER_SUPPLY_STATUS_FULL, -+}; -+ -+enum { -+ POWER_SUPPLY_HEALTH_UNKNOWN = 0, -+ POWER_SUPPLY_HEALTH_GOOD, -+ POWER_SUPPLY_HEALTH_OVERHEAT, -+ POWER_SUPPLY_HEALTH_DEAD, -+}; -+ -+enum { -+ POWER_SUPPLY_TECHNOLOGY_UNKNOWN = 0, -+ POWER_SUPPLY_TECHNOLOGY_NIMH, -+ POWER_SUPPLY_TECHNOLOGY_LION, -+ POWER_SUPPLY_TECHNOLOGY_LIPO, -+}; -+ -+enum { -+ POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN = 0, -+ POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL, -+ POWER_SUPPLY_CAPACITY_LEVEL_LOW, -+ POWER_SUPPLY_CAPACITY_LEVEL_NORMAL, -+ POWER_SUPPLY_CAPACITY_LEVEL_HIGH, -+ POWER_SUPPLY_CAPACITY_LEVEL_FULL, -+}; -+ -+enum power_supply_property { -+ /* Properties of type `int' */ -+ POWER_SUPPLY_PROP_STATUS = 0, -+ POWER_SUPPLY_PROP_HEALTH, -+ POWER_SUPPLY_PROP_PRESENT, -+ POWER_SUPPLY_PROP_ONLINE, -+ POWER_SUPPLY_PROP_TECHNOLOGY, -+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, -+ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, -+ POWER_SUPPLY_PROP_VOLTAGE_NOW, -+ POWER_SUPPLY_PROP_VOLTAGE_AVG, -+ POWER_SUPPLY_PROP_CURRENT_NOW, -+ POWER_SUPPLY_PROP_CURRENT_AVG, -+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, -+ POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN, -+ POWER_SUPPLY_PROP_CHARGE_FULL, -+ POWER_SUPPLY_PROP_CHARGE_EMPTY, -+ POWER_SUPPLY_PROP_CHARGE_NOW, -+ POWER_SUPPLY_PROP_CHARGE_AVG, -+ POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, -+ POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN, -+ POWER_SUPPLY_PROP_ENERGY_FULL, -+ POWER_SUPPLY_PROP_ENERGY_EMPTY, -+ POWER_SUPPLY_PROP_ENERGY_NOW, -+ POWER_SUPPLY_PROP_ENERGY_AVG, -+ POWER_SUPPLY_PROP_CAPACITY, /* in percents! */ -+ POWER_SUPPLY_PROP_CAPACITY_LEVEL, -+ POWER_SUPPLY_PROP_TEMP, -+ POWER_SUPPLY_PROP_TEMP_AMBIENT, -+ POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, -+ POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, -+ POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, -+ POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, -+ /* Properties of type `const char *' */ -+ POWER_SUPPLY_PROP_MODEL_NAME, -+}; -+ -+enum power_supply_type { -+ POWER_SUPPLY_TYPE_BATTERY = 0, -+ POWER_SUPPLY_TYPE_UPS, -+ POWER_SUPPLY_TYPE_MAINS, -+ POWER_SUPPLY_TYPE_USB, -+}; -+ -+union power_supply_propval { -+ int intval; -+ const char *strval; -+}; -+ -+struct power_supply { -+ const char *name; -+ enum power_supply_type type; -+ enum power_supply_property *properties; -+ size_t num_properties; -+ -+ char **supplied_to; -+ size_t num_supplicants; -+ -+ int (*get_property)(struct power_supply *psy, -+ enum power_supply_property psp, -+ union power_supply_propval *val); -+ void (*external_power_changed)(struct power_supply *psy); -+ -+ /* For APM emulation, think legacy userspace. */ -+ int use_for_apm; -+ -+ /* private */ -+ struct device *dev; -+ struct work_struct changed_work; -+ -+#ifdef CONFIG_LEDS_TRIGGERS -+ struct led_trigger *charging_full_trig; -+ char *charging_full_trig_name; -+ struct led_trigger *charging_trig; -+ char *charging_trig_name; -+ struct led_trigger *full_trig; -+ char *full_trig_name; -+ struct led_trigger *online_trig; -+ char *online_trig_name; -+#endif -+}; -+ -+/* -+ * This is recommended structure to specify static power supply parameters. -+ * Generic one, parametrizable for different power supplies. Power supply -+ * class itself does not use it, but that's what implementing most platform -+ * drivers, should try reuse for consistency. -+ */ -+ -+struct power_supply_info { -+ const char *name; -+ int technology; -+ int voltage_max_design; -+ int voltage_min_design; -+ int charge_full_design; -+ int charge_empty_design; -+ int energy_full_design; -+ int energy_empty_design; -+ int use_for_apm; -+}; -+ -+extern void power_supply_changed(struct power_supply *psy); -+extern int power_supply_am_i_supplied(struct power_supply *psy); -+ -+extern int power_supply_register(struct device *parent, -+ struct power_supply *psy); -+extern void power_supply_unregister(struct power_supply *psy); -+ -+/* For APM emulation, think legacy userspace. */ -+extern struct class *power_supply_class; -+ -+#endif /* __LINUX_POWER_SUPPLY_H__ */ diff --git a/meta/packages/linux/linux-rp-2.6.23+2.6.24-rc8/pxa27x_overlay-r8.patch b/meta/packages/linux/linux-rp-2.6.23+2.6.24-rc8/pxa27x_overlay-r8.patch deleted file mode 100644 index 693ad20453..0000000000 --- a/meta/packages/linux/linux-rp-2.6.23+2.6.24-rc8/pxa27x_overlay-r8.patch +++ /dev/null @@ -1,2427 +0,0 @@ - drivers/video/Kconfig | 18 - drivers/video/Makefile | 1 - drivers/video/pxafb.c | 305 +++++-- - drivers/video/pxafb.h | 65 + - drivers/video/pxafb_overlay.c | 1525 ++++++++++++++++++++++++++++++++++++ - include/asm-arm/arch-pxa/pxa-regs.h | 111 ++ - 6 files changed, 1969 insertions(+), 56 deletions(-) - ---- linux-2.6.24-rc1.orig/drivers/video/Kconfig -+++ linux-2.6.24-rc1/drivers/video/Kconfig -@@ -1718,6 +1718,24 @@ - - If unsure, say N. - -+choice -+ prompt "PXA LCD type" -+ depends on FB_PXA -+ -+config FB_PXA_LCD_QVGA -+ bool "QVGA(320x240)" -+ -+config FB_PXA_LCD_VGA -+ bool "VGA (640x480)" -+ -+endchoice -+ -+config FB_PXA_OVERLAY -+ tristate "PXA LCD overlay support" -+ depends on FB_PXA -+ ---help--- -+ Frame buffer overlay driver for PXA27x -+ - config FB_PXA_PARAMETERS - bool "PXA LCD command line parameters" - default n ---- linux-2.6.24-rc1.orig/drivers/video/Makefile -+++ linux-2.6.24-rc1/drivers/video/Makefile -@@ -96,6 +96,7 @@ - obj-$(CONFIG_FB_CIRRUS) += cirrusfb.o - obj-$(CONFIG_FB_ASILIANT) += asiliantfb.o - obj-$(CONFIG_FB_PXA) += pxafb.o -+obj-$(CONFIG_FB_PXA_OVERLAY) += pxafb_overlay.o - obj-$(CONFIG_FB_W100) += w100fb.o - obj-$(CONFIG_FB_AU1100) += au1100fb.o - obj-$(CONFIG_FB_AU1200) += au1200fb.o ---- linux-2.6.24-rc1.orig/drivers/video/pxafb.c -+++ linux-2.6.24-rc1/drivers/video/pxafb.c -@@ -59,17 +59,49 @@ - #define LCCR0_INVALID_CONFIG_MASK (LCCR0_OUM|LCCR0_BM|LCCR0_QDM|LCCR0_DIS|LCCR0_EFM|LCCR0_IUM|LCCR0_SFM|LCCR0_LDM|LCCR0_ENB) - #define LCCR3_INVALID_CONFIG_MASK (LCCR3_HSP|LCCR3_VSP|LCCR3_PCD|LCCR3_BPP) - -+wait_queue_head_t fcs_wait_eof; -+int fcs_in_eof; -+static DECLARE_MUTEX(fcs_lcd_sem); -+ - static void (*pxafb_backlight_power)(int); - static void (*pxafb_lcd_power)(int, struct fb_var_screeninfo *); - - static int pxafb_activate_var(struct fb_var_screeninfo *var, struct pxafb_info *); --static void set_ctrlr_state(struct pxafb_info *fbi, u_int state); -+void pxafb_set_ctrlr_state(struct pxafb_info *fbi, u_int state); - - #ifdef CONFIG_FB_PXA_PARAMETERS - #define PXAFB_OPTIONS_SIZE 256 - static char g_options[PXAFB_OPTIONS_SIZE] __devinitdata = ""; - #endif - -+static struct pxafb_rgb def_rgb_8 = { -+ red: { offset: 0, length: 8, }, -+ green: { offset: 0, length: 8, }, -+ blue: { offset: 0, length: 8, }, -+ transp: { offset: 0, length: 0, }, -+}; -+ -+static struct pxafb_rgb def_rgb_16 = { -+ red: { offset: 11, length: 5, }, -+ green: { offset: 5, length: 6, }, -+ blue: { offset: 0, length: 5, }, -+ transp: { offset: 0, length: 0, }, -+}; -+ -+static struct pxafb_rgb def_rgb_18 = { -+ red: { offset: 12, length: 6, }, -+ green: { offset: 6, length: 6, }, -+ blue: { offset: 0, length: 6, }, -+ transp: { offset: 0, length: 0, }, -+}; -+ -+static struct pxafb_rgb def_rgb_24 = { -+ red: { offset: 16, length: 8, }, -+ green: { offset: 8, length: 8, }, -+ blue: { offset: 0, length: 8, }, -+ transp: { offset: 0, length: 0, }, -+}; -+ - static inline void pxafb_schedule_work(struct pxafb_info *fbi, u_int state) - { - unsigned long flags; -@@ -209,6 +241,10 @@ - case 4: ret = LCCR3_4BPP; break; - case 8: ret = LCCR3_8BPP; break; - case 16: ret = LCCR3_16BPP; break; -+ case 18: ret = LCCR3_18BPP; break; -+ case 19: ret = LCCR3_19BPP; break; -+ case 24: ret = LCCR3_24BPP; break; -+ case 25: ret = LCCR3_25BPP; break; - } - return ret; - } -@@ -320,18 +356,34 @@ - * The pixel packing format is described on page 7-11 of the - * PXA2XX Developer's Manual. - */ -- if (var->bits_per_pixel == 16) { -- var->red.offset = 11; var->red.length = 5; -- var->green.offset = 5; var->green.length = 6; -- var->blue.offset = 0; var->blue.length = 5; -- var->transp.offset = var->transp.length = 0; -- } else { -- var->red.offset = var->green.offset = var->blue.offset = var->transp.offset = 0; -- var->red.length = 8; -- var->green.length = 8; -- var->blue.length = 8; -- var->transp.length = 0; -- } -+ switch (var->bits_per_pixel) { -+ case 16: -+ /* 2 pixels per line */ -+ var->red = def_rgb_16.red; -+ var->green = def_rgb_16.green; -+ var->blue = def_rgb_16.blue; -+ var->transp = def_rgb_16.transp; -+ break; -+ case 18: -+ case 19: -+ var->red = def_rgb_18.red; -+ var->green = def_rgb_18.green; -+ var->blue = def_rgb_18.blue; -+ var->transp = def_rgb_18.transp; -+ break; -+ case 24: -+ case 25: -+ var->red = def_rgb_24.red; -+ var->green = def_rgb_24.green; -+ var->blue = def_rgb_24.blue; -+ var->transp = def_rgb_24.transp; -+ break; -+ default: -+ var->red = def_rgb_8.red; -+ var->green = def_rgb_8.green; -+ var->blue = def_rgb_8.blue; -+ var->transp = def_rgb_8.transp; -+ } - - #ifdef CONFIG_CPU_FREQ - pr_debug("pxafb: dma period = %d ps, clock = %d kHz\n", -@@ -345,7 +397,7 @@ - static inline void pxafb_set_truecolor(u_int is_true_color) - { - pr_debug("pxafb: true_color = %d\n", is_true_color); -- // do your machine-specific setup if needed -+ /* do your machine-specific setup if needed */ - } - - /* -@@ -360,7 +412,8 @@ - - pr_debug("pxafb: set_par\n"); - -- if (var->bits_per_pixel == 16) -+ if (var->bits_per_pixel == 16 || var->bits_per_pixel == 18 ||var->bits_per_pixel == 19 -+ || var->bits_per_pixel == 24 || var->bits_per_pixel == 25) - fbi->fb.fix.visual = FB_VISUAL_TRUECOLOR; - else if (!fbi->cmap_static) - fbi->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; -@@ -373,12 +426,25 @@ - fbi->fb.fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR; - } - -- fbi->fb.fix.line_length = var->xres_virtual * -- var->bits_per_pixel / 8; -- if (var->bits_per_pixel == 16) -- fbi->palette_size = 0; -- else -- fbi->palette_size = var->bits_per_pixel == 1 ? 4 : 1 << var->bits_per_pixel; -+ switch (var->bits_per_pixel) { -+ case 16: -+ fbi->fb.fix.line_length = var->xres_virtual * 2; -+ fbi->palette_size = 0; -+ break; -+ case 18: -+ case 19: -+ fbi->fb.fix.line_length = var->xres_virtual * 3; -+ fbi->palette_size = 0; -+ break; -+ case 24: -+ case 25: -+ fbi->fb.fix.line_length = var->xres_virtual * 4; -+ fbi->palette_size = 0; -+ break; -+ default: -+ fbi->fb.fix.line_length = var->xres_virtual * var->bits_per_pixel / 8; -+ fbi->palette_size = var->bits_per_pixel == 1 ? 4 : 1 << var->bits_per_pixel; -+ } - - if ((fbi->lccr4 & LCCR4_PAL_FOR_MASK) == LCCR4_PAL_FOR_0) - palette_mem_size = fbi->palette_size * sizeof(u16); -@@ -395,7 +461,8 @@ - */ - pxafb_set_truecolor(fbi->fb.fix.visual == FB_VISUAL_TRUECOLOR); - -- if (fbi->fb.var.bits_per_pixel == 16) -+ if (fbi->fb.var.bits_per_pixel == 16 || fbi->fb.var.bits_per_pixel == 18 ||fbi->fb.var.bits_per_pixel == 19 -+ || fbi->fb.var.bits_per_pixel == 24 || fbi->fb.var.bits_per_pixel == 25) - fb_dealloc_cmap(&fbi->fb.cmap); - else - fb_alloc_cmap(&fbi->fb.cmap, 1<<fbi->fb.var.bits_per_pixel, 0); -@@ -441,7 +508,7 @@ - * 16 bpp mode does not really use the palette, so this will not - * blank the display in all modes. - */ --static int pxafb_blank(int blank, struct fb_info *info) -+int pxafb_blank(int blank, struct fb_info *info) - { - struct pxafb_info *fbi = (struct pxafb_info *)info; - int i; -@@ -458,19 +525,20 @@ - for (i = 0; i < fbi->palette_size; i++) - pxafb_setpalettereg(i, 0, 0, 0, 0, info); - -- pxafb_schedule_work(fbi, C_DISABLE); -- //TODO if (pxafb_blank_helper) pxafb_blank_helper(blank); -+ pxafb_schedule_work(fbi, C_BLANK); -+ /* TODO if (pxafb_blank_helper) pxafb_blank_helper(blank); */ - break; - - case FB_BLANK_UNBLANK: -- //TODO if (pxafb_blank_helper) pxafb_blank_helper(blank); -+ /* TODO if (pxafb_blank_helper) pxafb_blank_helper(blank); */ - if (fbi->fb.fix.visual == FB_VISUAL_PSEUDOCOLOR || - fbi->fb.fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR) - fb_set_cmap(&fbi->fb.cmap, info); -- pxafb_schedule_work(fbi, C_ENABLE); -+ pxafb_schedule_work(fbi, C_UNBLANK); - } - return 0; - } -+EXPORT_SYMBOL(pxafb_blank); - - static int pxafb_mmap(struct fb_info *info, - struct vm_area_struct *vma) -@@ -606,6 +674,10 @@ - case 4: - case 8: - case 16: -+ case 18: -+ case 19: -+ case 24: -+ case 25: - break; - default: - printk(KERN_ERR "%s: invalid bit depth %d\n", -@@ -637,7 +709,10 @@ - - new_regs.lccr0 = fbi->lccr0 | - (LCCR0_LDM | LCCR0_SFM | LCCR0_IUM | LCCR0_EFM | -- LCCR0_QDM | LCCR0_BM | LCCR0_OUM); -+#ifdef CONFIG_PXA27x /* Enable overlay for PXA27x */ -+ LCCR0_OUC | LCCR0_CMDIM | LCCR0_RDSTM | -+#endif -+ LCCR0_QDM | LCCR0_BM | LCCR0_OUM); - - new_regs.lccr1 = - LCCR1_DisWdth(var->xres) + -@@ -696,7 +771,7 @@ - - fbi->dmadesc_fbhigh_cpu->fsadr = fbi->screen_dma; - fbi->dmadesc_fbhigh_cpu->fidr = 0; -- fbi->dmadesc_fbhigh_cpu->ldcmd = BYTES_PER_PANEL; -+ fbi->dmadesc_fbhigh_cpu->ldcmd = BYTES_PER_PANEL | LDCMD_EOFINT; - - fbi->dmadesc_palette_cpu->fsadr = fbi->palette_dma; - fbi->dmadesc_palette_cpu->fidr = 0; -@@ -708,7 +783,8 @@ - sizeof(u32); - fbi->dmadesc_palette_cpu->ldcmd |= LDCMD_PAL; - -- if (var->bits_per_pixel == 16) { -+ if (var->bits_per_pixel == 16 || var->bits_per_pixel == 18 ||var->bits_per_pixel == 19 -+ || var->bits_per_pixel == 24 || var->bits_per_pixel == 25) { - /* palette shouldn't be loaded in true-color mode */ - fbi->dmadesc_fbhigh_cpu->fdadr = fbi->dmadesc_fbhigh_dma; - fbi->fdadr0 = fbi->dmadesc_fbhigh_dma; /* no pal just fbhigh */ -@@ -763,8 +839,8 @@ - } - - /* -- * NOTE! The following functions are purely helpers for set_ctrlr_state. -- * Do not call them directly; set_ctrlr_state does the correct serialisation -+ * NOTE! The following functions are purely helpers for pxafb_set_ctrlr_state. -+ * Do not call them directly; pxafb_set_ctrlr_state does the correct serialisation - * to ensure that things happen in the right way 100% of time time. - * -- rmk - */ -@@ -786,7 +862,8 @@ - - static void pxafb_setup_gpio(struct pxafb_info *fbi) - { -- int gpio, ldd_bits; -+ int gpio; -+ int ldd_bits = 0; - unsigned int lccr0 = fbi->lccr0; - - /* -@@ -796,28 +873,56 @@ - /* 4 bit interface */ - if ((lccr0 & LCCR0_CMS) == LCCR0_Mono && - (lccr0 & LCCR0_SDS) == LCCR0_Sngl && -- (lccr0 & LCCR0_DPD) == LCCR0_4PixMono) -+ (lccr0 & LCCR0_DPD) == LCCR0_4PixMono) { - ldd_bits = 4; -- -+ } - /* 8 bit interface */ - else if (((lccr0 & LCCR0_CMS) == LCCR0_Mono && - ((lccr0 & LCCR0_SDS) == LCCR0_Dual || (lccr0 & LCCR0_DPD) == LCCR0_8PixMono)) || - ((lccr0 & LCCR0_CMS) == LCCR0_Color && -- (lccr0 & LCCR0_PAS) == LCCR0_Pas && (lccr0 & LCCR0_SDS) == LCCR0_Sngl)) -+ (lccr0 & LCCR0_PAS) == LCCR0_Pas && (lccr0 & LCCR0_SDS) == LCCR0_Sngl)) { - ldd_bits = 8; -- -+ } - /* 16 bit interface */ -- else if ((lccr0 & LCCR0_CMS) == LCCR0_Color && -- ((lccr0 & LCCR0_SDS) == LCCR0_Dual || (lccr0 & LCCR0_PAS) == LCCR0_Act)) -- ldd_bits = 16; -+ else if ((lccr0 & LCCR0_CMS) == LCCR0_Color && -+ ((lccr0 & LCCR0_SDS) == LCCR0_Dual || (lccr0 & LCCR0_PAS) == LCCR0_Act)) { -+ switch (fbi->fb.var.bits_per_pixel) { -+ case 16: -+#ifdef CONFIG_PXA27x -+ /* bits 58-77 */ -+ GPDR1 |= (0x3f << 26); -+ GPDR2 |= 0x00003fff; - -+ GAFR1_U = (GAFR1_U & ~(0xfff << 20)) | (0xaaa << 20); -+ GAFR2_L = (GAFR2_L & 0xf0000000) | 0x0aaaaaaa; -+#endif -+ ldd_bits = 16; -+ break; -+ case 18: -+ case 19: -+ case 24: -+ case 25: -+#ifdef CONFIG_PXA27x -+ /* bits 58-77 and 86, 87 */ -+ GPDR1 |= (0x3f << 26); -+ GPDR2 |= 0x00c03fff; -+ -+ GAFR1_U = (GAFR1_U & ~(0xfff << 20)) | (0xaaa << 20); -+ GAFR2_L = (GAFR2_L & 0xf0000000) | 0x0aaaaaaa; -+ GAFR2_U = (GAFR2_U & 0xffff0fff) | 0xa000; -+#endif -+ ldd_bits = 25; -+ break; -+ } -+ } - else { - printk(KERN_ERR "pxafb_setup_gpio: unable to determine bits per pixel\n"); - return; - } - -- for (gpio = 58; ldd_bits; gpio++, ldd_bits--) -+ for (gpio = 58; ldd_bits > 0; gpio++, ldd_bits--) { - pxa_gpio_mode(gpio | GPIO_ALT_FN_2_OUT); -+ } - pxa_gpio_mode(GPIO74_LCD_FCLK_MD); - pxa_gpio_mode(GPIO75_LCD_LCLK_MD); - pxa_gpio_mode(GPIO76_LCD_PCLK_MD); -@@ -837,6 +942,7 @@ - /* enable LCD controller clock */ - clk_enable(fbi->clk); - -+ down(&fcs_lcd_sem); - /* Sequence from 11.7.10 */ - LCCR3 = fbi->reg_lccr3; - LCCR2 = fbi->reg_lccr2; -@@ -847,6 +953,8 @@ - FDADR1 = fbi->fdadr1; - LCCR0 |= LCCR0_ENB; - -+ up(&fcs_lcd_sem); -+ - pr_debug("FDADR0 0x%08x\n", (unsigned int) FDADR0); - pr_debug("FDADR1 0x%08x\n", (unsigned int) FDADR1); - pr_debug("LCCR0 0x%08x\n", (unsigned int) LCCR0); -@@ -862,6 +970,7 @@ - - pr_debug("pxafb: disabling LCD controller\n"); - -+ down(&fcs_lcd_sem); - set_current_state(TASK_UNINTERRUPTIBLE); - add_wait_queue(&fbi->ctrlr_wait, &wait); - -@@ -871,6 +980,7 @@ - - schedule_timeout(200 * HZ / 1000); - remove_wait_queue(&fbi->ctrlr_wait, &wait); -+ up(&fcs_lcd_sem); - - /* disable LCD controller clock */ - clk_disable(fbi->clk); -@@ -888,6 +998,11 @@ - LCCR0 |= LCCR0_LDM; - wake_up(&fbi->ctrlr_wait); - } -+ if (lcsr & LCSR_EOF && fcs_in_eof) { -+ LCCR0 |= LCCR0_EFM; -+ fcs_in_eof = 0; -+ wake_up(&fcs_wait_eof); -+ } - - LCSR = lcsr; - return IRQ_HANDLED; -@@ -898,7 +1013,7 @@ - * sleep when disabling the LCD controller, or if we get two contending - * processes trying to alter state. - */ --static void set_ctrlr_state(struct pxafb_info *fbi, u_int state) -+void pxafb_set_ctrlr_state(struct pxafb_info *fbi, u_int state) - { - u_int old_state; - -@@ -920,7 +1035,9 @@ - */ - if (old_state != C_DISABLE && old_state != C_DISABLE_PM) { - fbi->state = state; -- //TODO __pxafb_lcd_power(fbi, 0); -+ /* TODO __pxafb_lcd_power(fbi, 0); */ -+ if(fbi->set_overlay_ctrlr_state) -+ fbi->set_overlay_ctrlr_state(fbi, C_DISABLE); - pxafb_disable_controller(fbi); - } - break; -@@ -934,6 +1051,8 @@ - fbi->state = state; - __pxafb_backlight_power(fbi, 0); - __pxafb_lcd_power(fbi, 0); -+ if(fbi->set_overlay_ctrlr_state) -+ fbi->set_overlay_ctrlr_state(fbi, C_DISABLE); - if (old_state != C_DISABLE_CLKCHANGE) - pxafb_disable_controller(fbi); - } -@@ -947,7 +1066,9 @@ - if (old_state == C_DISABLE_CLKCHANGE) { - fbi->state = C_ENABLE; - pxafb_enable_controller(fbi); -- //TODO __pxafb_lcd_power(fbi, 1); -+ /* TODO __pxafb_lcd_power(fbi, 1); */ -+ if(fbi->set_overlay_ctrlr_state) -+ fbi->set_overlay_ctrlr_state(fbi, C_ENABLE); - } - break; - -@@ -959,9 +1080,13 @@ - */ - if (old_state == C_ENABLE) { - __pxafb_lcd_power(fbi, 0); -+ if(fbi->set_overlay_ctrlr_state) -+ fbi->set_overlay_ctrlr_state(fbi, C_DISABLE); - pxafb_disable_controller(fbi); - pxafb_setup_gpio(fbi); - pxafb_enable_controller(fbi); -+ if(fbi->set_overlay_ctrlr_state) -+ fbi->set_overlay_ctrlr_state(fbi, C_ENABLE); - __pxafb_lcd_power(fbi, 1); - } - break; -@@ -987,11 +1112,46 @@ - pxafb_enable_controller(fbi); - __pxafb_lcd_power(fbi, 1); - __pxafb_backlight_power(fbi, 1); -+ if(fbi->set_overlay_ctrlr_state) -+ fbi->set_overlay_ctrlr_state(fbi, C_ENABLE); - } - break; -+ -+ case C_BLANK: -+ /* -+ * Disable controller, blank overlays if exist. -+ */ -+ if ((old_state != C_DISABLE) && (old_state != C_BLANK)) { -+ fbi->state = state; -+ __pxafb_backlight_power(fbi, 0); -+ __pxafb_lcd_power(fbi, 0); -+ if(fbi->set_overlay_ctrlr_state) -+ fbi->set_overlay_ctrlr_state(fbi, C_BLANK); -+ if (old_state != C_DISABLE_CLKCHANGE) -+ pxafb_disable_controller(fbi); -+ } -+ break; -+ -+ case C_UNBLANK: -+ /* -+ * Power up the LCD screen, enable controller, and -+ * turn on the backlight, unblank overlays if exist. -+ */ -+ if ((old_state != C_ENABLE) && (old_state != C_UNBLANK)) { -+ fbi->state = C_UNBLANK; -+ pxafb_setup_gpio(fbi); -+ pxafb_enable_controller(fbi); -+ __pxafb_lcd_power(fbi, 1); -+ __pxafb_backlight_power(fbi, 1); -+ if(fbi->set_overlay_ctrlr_state) -+ fbi->set_overlay_ctrlr_state(fbi, C_UNBLANK); -+ } -+ break; -+ - } - up(&fbi->ctrlr_sem); - } -+EXPORT_SYMBOL(pxafb_set_ctrlr_state); - - /* - * Our LCD controller task (which is called when we blank or unblank) -@@ -1003,7 +1163,7 @@ - container_of(work, struct pxafb_info, task); - u_int state = xchg(&fbi->task_state, -1); - -- set_ctrlr_state(fbi, state); -+ pxafb_set_ctrlr_state(fbi, state); - } - - #ifdef CONFIG_CPU_FREQ -@@ -1018,19 +1178,29 @@ - pxafb_freq_transition(struct notifier_block *nb, unsigned long val, void *data) - { - struct pxafb_info *fbi = TO_INF(nb, freq_transition); -- //TODO struct cpufreq_freqs *f = data; -+ /* TODO struct cpufreq_freqs *f = data; */ -+ struct cpufreq_freqs *clkinfo; - u_int pcd; -+ u_int lccr3; - - switch (val) { - case CPUFREQ_PRECHANGE: -- set_ctrlr_state(fbi, C_DISABLE_CLKCHANGE); -+ pxafb_set_ctrlr_state(fbi, C_DISABLE_CLKCHANGE); - break; - - case CPUFREQ_POSTCHANGE: -- pcd = get_pcd(fbi, fbi->fb.var.pixclock); -+ clkinfo = (struct cpufreq_freqs *)data; -+ /* If leaving a 13kHz state with the LCD sustained */ -+ if ((clkinfo->old == 13000)) -+ break; -+ -+ pcd = get_pcd(fbi->fb.var.pixclock); -+ lccr3 = fbi->reg_lccr3; - set_hsync_time(fbi, pcd); - fbi->reg_lccr3 = (fbi->reg_lccr3 & ~0xff) | LCCR3_PixClkDiv(pcd); -- set_ctrlr_state(fbi, C_ENABLE_CLKCHANGE); -+ pxafb_set_ctrlr_state(fbi, C_ENABLE_CLKCHANGE); -+ if (lccr3 != fbi->reg_lccr3 && !((LCCR0 & LCCR0_DIS) || !(LCCR0 & LCCR0_ENB))) -+ LCCR3 = fbi->reg_lccr3; - break; - } - return 0; -@@ -1049,7 +1219,7 @@ - printk(KERN_DEBUG "min dma period: %d ps, " - "new clock %d kHz\n", pxafb_display_dma_period(var), - policy->max); -- // TODO: fill in min/max values -+ /* TODO: fill in min/max values */ - break; - #if 0 - case CPUFREQ_NOTIFY: -@@ -1075,7 +1245,7 @@ - { - struct pxafb_info *fbi = platform_get_drvdata(dev); - -- set_ctrlr_state(fbi, C_DISABLE_PM); -+ pxafb_set_ctrlr_state(fbi, C_DISABLE_PM); - return 0; - } - -@@ -1083,7 +1253,11 @@ - { - struct pxafb_info *fbi = platform_get_drvdata(dev); - -- set_ctrlr_state(fbi, C_ENABLE_PM); -+ pxafb_set_ctrlr_state(fbi, C_ENABLE_PM); -+//RP#ifdef CONFIG_PXA27x -+//RP LCCR4 |= (1 << 31); /* Disable the PCD Divisor, PCDDIV */ -+//RP LCCR4 |= (5 << 17); /* Undocumented feature */ -+//RP#endif - return 0; - } - #else -@@ -1197,11 +1371,21 @@ - fbi->task_state = (u_char)-1; - - for (i = 0; i < inf->num_modes; i++) { -- smemlen = mode[i].xres * mode[i].yres * mode[i].bpp / 8; -+ if (mode[i].bpp <= 16) { /* 8, 16 bpp */ -+ smemlen = mode[i].xres * mode[i].yres * mode[i].bpp / 8; -+ } else if ( mode[i].bpp > 19 ) { /* 24, 25 bpp */ -+ smemlen = mode[i].xres * mode[i].yres * 4; -+ } else { /* 18, 19 bpp */ -+ /* packed format */ -+ smemlen = mode[i].xres * mode[i].yres * 3; -+ } -+ - if (smemlen > fbi->fb.fix.smem_len) - fbi->fb.fix.smem_len = smemlen; - } - -+ fbi->set_overlay_ctrlr_state = NULL; -+ - init_waitqueue_head(&fbi->ctrlr_wait); - INIT_WORK(&fbi->task, pxafb_task); - init_MUTEX(&fbi->ctrlr_sem); -@@ -1268,6 +1452,10 @@ - case 4: - case 8: - case 16: -+ case 18: -+ case 19: -+ case 24: -+ case 25: - inf->modes[0].bpp = bpp; - dev_info(dev, "overriding bit depth: %d\n", bpp); - break; -@@ -1416,7 +1604,7 @@ - fbi = pxafb_init_fbinfo(&dev->dev); - if (!fbi) { - dev_err(&dev->dev, "Failed to initialize framebuffer device\n"); -- ret = -ENOMEM; // only reason for pxafb_init_fbinfo to fail is kmalloc -+ ret = -ENOMEM; /* only reason for pxafb_init_fbinfo to fail is kmalloc */ - goto failed; - } - -@@ -1451,7 +1639,7 @@ - } - - #ifdef CONFIG_PM -- // TODO -+ /* TODO */ - #endif - - #ifdef CONFIG_CPU_FREQ -@@ -1464,7 +1652,12 @@ - /* - * Ok, now enable the LCD controller - */ -- set_ctrlr_state(fbi, C_ENABLE); -+ pxafb_set_ctrlr_state(fbi, C_ENABLE); -+//#ifdef CONFIG_PXA27x -+// LCCR4 |= (1 << 31); /* Disabel the PCD Divisor, PCDDIV */ -+// LCCR4 |= (5 << 17); /* Undocumented feature */ -+//#endif -+ init_waitqueue_head(&fcs_wait_eof); - - return 0; - ---- linux-2.6.24-rc1.orig/drivers/video/pxafb.h -+++ linux-2.6.24-rc1/drivers/video/pxafb.h -@@ -29,6 +29,60 @@ - unsigned int lccr3; - }; - -+struct pxafb_rgb { -+ struct fb_bitfield red; -+ struct fb_bitfield green; -+ struct fb_bitfield blue; -+ struct fb_bitfield transp; -+}; -+ -+#ifdef CONFIG_PXA27x -+/* PXA Overlay Framebuffer Support */ -+struct overlayfb_info -+{ -+ struct fb_info fb; -+ -+ struct fb_var_screeninfo old_var; -+ -+ struct semaphore mutex; -+ unsigned long refcount; -+ -+ struct pxafb_info *basefb; -+ -+ unsigned long map_cpu; -+ unsigned long screen_cpu; -+ unsigned long palette_cpu; -+ unsigned long map_size; -+ unsigned long palette_size; -+ -+ dma_addr_t screen_dma; -+ dma_addr_t map_dma; -+ dma_addr_t palette_dma; -+ -+ volatile u_char state; -+ -+ /* overlay specific info */ -+ unsigned long xpos; /* screen position (x, y)*/ -+ unsigned long ypos; -+ unsigned long format; -+ -+ /* additional */ -+ union { -+ struct pxafb_dma_descriptor *dma0; -+ struct pxafb_dma_descriptor *dma1; -+ struct { -+ struct pxafb_dma_descriptor *dma2; -+ struct pxafb_dma_descriptor *dma3; -+ struct pxafb_dma_descriptor *dma4; -+ }; -+ struct { -+ struct pxafb_dma_descriptor *dma5_pal; -+ struct pxafb_dma_descriptor *dma5_frame; -+ }; -+ }; -+}; -+#endif -+ - /* PXA LCD DMA descriptor */ - struct pxafb_dma_descriptor { - unsigned int fdadr; -@@ -90,6 +144,14 @@ - wait_queue_head_t ctrlr_wait; - struct work_struct task; - -+#ifdef CONFIG_PXA27x -+ /* PXA Overlay Framebuffer Support */ -+ struct overlayfb_info *overlay1fb; -+ struct overlayfb_info *overlay2fb; -+ struct overlayfb_info *cursorfb; -+#endif -+ void (*set_overlay_ctrlr_state)(struct pxafb_info *, u_int); -+ - #ifdef CONFIG_CPU_FREQ - struct notifier_block freq_transition; - struct notifier_block freq_policy; -@@ -109,6 +171,9 @@ - #define C_DISABLE_PM (5) - #define C_ENABLE_PM (6) - #define C_STARTUP (7) -+#define C_BLANK (8) -+#define C_UNBLANK (9) -+ - - #define PXA_NAME "PXA" - ---- /dev/null -+++ linux-2.6.24-rc1/drivers/video/pxafb_overlay.c -@@ -0,0 +1,1525 @@ -+/* -+ * linux/drivers/video/pxafb_overlay.c -+ * -+ * Copyright (c) 2004, Intel Corporation -+ * -+ * Code Status: -+ * 2004/10/28: <yan.yin@intel.com> -+ * - Ported to 2.6 kernel -+ * - Made overlay driver a loadable module -+ * - Merged overlay optimized patch -+ * 2004/03/10: <stanley.cai@intel.com> -+ * - Fixed Bugs -+ * - Added workaround for overlay1&2 -+ * 2003/08/27: <yu.tang@intel.com> -+ * - Added Overlay 1 & Overlay2 & Hardware Cursor support -+ * -+ * -+ * This software program is licensed subject to the GNU Lesser General -+ * Public License (LGPL). Version 2.1, February 1999, available at -+ * http://www.gnu.org/copyleft/lesser.html -+ * -+ * Intel PXA27x LCD Controller Frame Buffer Overlay Driver -+ * -+ * -+ */ -+ -+#include <linux/module.h> -+#include <linux/moduleparam.h> -+#include <linux/kernel.h> -+#include <linux/sched.h> -+#include <linux/errno.h> -+#include <linux/string.h> -+#include <linux/interrupt.h> -+#include <linux/slab.h> -+#include <linux/fb.h> -+#include <linux/delay.h> -+#include <linux/init.h> -+#include <linux/ioport.h> -+#include <linux/cpufreq.h> -+#include <linux/device.h> -+#include <linux/platform_device.h> -+#include <linux/dma-mapping.h> -+ -+#include <asm/hardware.h> -+#include <asm/io.h> -+#include <asm/irq.h> -+#include <asm/uaccess.h> -+#include <asm/arch/bitfield.h> -+#include <asm/arch/pxafb.h> -+#include <asm/arch/pxa-regs.h> -+ -+#include "pxafb.h" -+ -+/* LCD enhancement : Overlay 1 & 2 & Hardware Cursor */ -+ -+/* -+ * LCD enhancement : Overlay 1 -+ * -+ * Features: -+ * - support 16bpp (No palette) -+ */ -+/* -+ * debugging? -+ */ -+#define DEBUG 0 -+ -+#ifdef DEBUG -+#define dbg(fmt,arg...) printk(KERN_ALERT "%s(): " fmt "\n", __FUNCTION__, ##arg) -+#else -+#define dbg(fmt,arg...) -+#endif -+ -+static int overlay1fb_enable(struct fb_info *info); -+static int overlay2fb_enable(struct fb_info *info); -+static int cursorfb_enable(struct fb_info *info); -+ -+static int overlay1fb_disable(struct fb_info *info); -+static int overlay2fb_disable(struct fb_info *info); -+static int cursorfb_disable(struct fb_info *info); -+ -+static int overlay1fb_blank(int blank, struct fb_info *info); -+static int overlay2fb_blank(int blank, struct fb_info *info); -+static int cursorfb_blank(int blank, struct fb_info *info); -+ -+extern void pxafb_set_ctrlr_state(struct pxafb_info *fbi, u_int state); -+extern int pxafb_blank(int blank, struct fb_info *info); -+ -+static struct pxafb_rgb def_rgb_18 = { -+ red: { offset: 12, length: 6, }, -+ green: { offset: 6, length: 6, }, -+ blue: { offset: 0, length: 6, }, -+ transp: { offset: 0, length: 0, }, -+}; -+ -+static struct pxafb_rgb def_rgbt_16 = { -+ red: { offset: 10, length: 5, }, -+ green: { offset: 5, length: 5, }, -+ blue: { offset: 0, length: 5, }, -+ transp: { offset: 15, length: 1, }, -+}; -+ -+static struct pxafb_rgb def_rgbt_19 = { -+ red: { offset: 12, length: 6, }, -+ green: { offset: 6, length: 6, }, -+ blue: { offset: 0, length: 6, }, -+ transp: { offset: 18, length: 1, }, -+}; -+ -+static struct pxafb_rgb def_rgbt_24 = { -+ red: { offset: 16, length: 7, }, -+ green: { offset: 8, length: 8, }, -+ blue: { offset: 0, length: 8, }, -+ transp: { offset: 0, length: 0, }, -+}; -+ -+static struct pxafb_rgb def_rgbt_25 = { -+ red: { offset: 16, length: 8, }, -+ green: { offset: 8, length: 8, }, -+ blue: { offset: 0, length: 8, }, -+ transp: { offset: 24, length: 1, }, -+}; -+ -+#define CLEAR_LCD_INTR(reg, intr) do { \ -+ reg = (intr); \ -+}while(0) -+ -+#define WAIT_FOR_LCD_INTR(reg,intr,timeout) ({ \ -+ int __done =0; \ -+ int __t = timeout; \ -+ while (__t) { \ -+ __done = (reg) & (intr); \ -+ if (__done) break; \ -+ mdelay(10); \ -+ __t--; \ -+ } \ -+ if (!__t) dbg("wait " #intr " timeount");\ -+ __done; \ -+}) -+ -+#define DISABLE_OVERLAYS(fbi) do { \ -+ if (fbi->overlay1fb && (fbi->overlay1fb->state == C_ENABLE)) { \ -+ overlay1fb_disable((struct fb_info*)fbi->overlay1fb); \ -+ } \ -+ if (fbi->overlay2fb && (fbi->overlay2fb->state == C_ENABLE)) { \ -+ overlay2fb_disable((struct fb_info*)fbi->overlay2fb); \ -+ } \ -+ if (fbi->cursorfb && (fbi->cursorfb->state == C_ENABLE)) { \ -+ cursorfb_disable((struct fb_info*)fbi->cursorfb); \ -+ } \ -+}while(0) -+ -+#define ENABLE_OVERLAYS(fbi) do { \ -+ if (fbi->overlay1fb && (fbi->overlay1fb->state == C_DISABLE)) { \ -+ overlay1fb_enable((struct fb_info*)fbi->overlay1fb); \ -+ } \ -+ if (fbi->overlay2fb && (fbi->overlay2fb->state == C_DISABLE)) { \ -+ overlay2fb_enable((struct fb_info*)fbi->overlay2fb); \ -+ } \ -+ if (fbi->cursorfb && (fbi->cursorfb->state == C_DISABLE)) { \ -+ cursorfb_enable((struct fb_info*)fbi->cursorfb); \ -+ } \ -+}while(0) -+ -+#define BLANK_OVERLAYS(fbi) do { \ -+ if (fbi->overlay1fb && (fbi->overlay1fb->state == C_ENABLE)) { \ -+ overlay1fb_disable((struct fb_info*)fbi->overlay1fb); \ -+ fbi->overlay1fb->state = C_BLANK; \ -+ } \ -+ if (fbi->overlay2fb && (fbi->overlay2fb->state == C_ENABLE)) { \ -+ overlay2fb_disable((struct fb_info*)fbi->overlay2fb); \ -+ fbi->overlay2fb->state = C_BLANK; \ -+ } \ -+ if (fbi->cursorfb && (fbi->cursorfb->state == C_ENABLE)) { \ -+ cursorfb_disable((struct fb_info*)fbi->cursorfb); \ -+ fbi->cursorfb->state = C_BLANK; \ -+ } \ -+}while(0) -+ -+#define UNBLANK_OVERLAYS(fbi) do { \ -+ if (fbi->overlay1fb && (fbi->overlay1fb->state == C_BLANK)) { \ -+ overlay1fb_enable((struct fb_info*)fbi->overlay1fb); \ -+ fbi->overlay1fb->state = C_ENABLE; \ -+ } \ -+ if (fbi->overlay2fb && (fbi->overlay2fb->state == C_BLANK)) { \ -+ overlay2fb_enable((struct fb_info*)fbi->overlay2fb); \ -+ fbi->overlay2fb->state = C_ENABLE; \ -+ } \ -+ if (fbi->cursorfb && (fbi->cursorfb->state == C_BLANK)) { \ -+ cursorfb_enable((struct fb_info*)fbi->cursorfb); \ -+ fbi->cursorfb->state = C_ENABLE; \ -+ } \ -+}while(0) -+ -+static int overlay1fb_open(struct fb_info *info, int user) -+{ -+ struct overlayfb_info *fbi = (struct overlayfb_info*) info; -+ int ret = 0; -+ -+/* If basefb is disable, enable fb. */ -+ if (fbi->basefb && fbi->basefb->state != C_ENABLE) -+ pxafb_blank(VESA_NO_BLANKING, (struct fb_info *)(fbi->basefb)); -+ -+ down(&fbi->mutex); -+ -+ if (fbi->refcount) -+ ret = -EACCES; -+ else -+ fbi->refcount ++; -+ -+ up(&fbi->mutex); -+ -+ /* Initialize the variables in overlay1 framebuffer. */ -+ fbi->fb.var.xres = fbi->fb.var.yres = 0; -+ fbi->fb.var.bits_per_pixel = 0; -+ -+ return ret; -+} -+ -+static int overlay1fb_release(struct fb_info *info, int user) -+{ -+ struct overlayfb_info *fbi = (struct overlayfb_info*) info; -+ down(&fbi->mutex); -+ -+ if (fbi->refcount) -+ fbi->refcount --; -+ -+ up(&fbi->mutex); -+ /* disable overlay when released */ -+ overlay1fb_blank(1, info); -+ -+ return 0; -+} -+ -+static int overlay1fb_map_video_memory(struct fb_info *info) -+{ -+ struct overlayfb_info *fbi = (struct overlayfb_info*) info; -+ -+ if (fbi->map_cpu) -+ dma_free_writecombine(NULL, fbi->map_size, (void*)fbi->map_cpu, fbi->map_dma); -+ fbi->map_size = PAGE_ALIGN(fbi->fb.fix.smem_len + PAGE_SIZE); -+ -+ fbi->map_cpu = (unsigned long)dma_alloc_writecombine(NULL, fbi->map_size, -+ &fbi->map_dma, GFP_KERNEL ); -+ -+ if (!fbi->map_cpu) return -ENOMEM; -+ -+ fbi->screen_cpu = fbi->map_cpu + PAGE_SIZE; -+ fbi->screen_dma = fbi->map_dma + PAGE_SIZE; -+ -+ fbi->fb.fix.smem_start = fbi->screen_dma; -+ -+ /* setup dma descriptor */ -+ fbi->dma1 = (struct pxafb_dma_descriptor*) -+ (fbi->screen_cpu - sizeof(struct pxafb_dma_descriptor)); -+ -+ fbi->dma1->fdadr = (fbi->screen_dma - sizeof(struct pxafb_dma_descriptor)); -+ fbi->dma1->fsadr = fbi->screen_dma; -+ fbi->dma1->fidr = 0; -+ fbi->dma1->ldcmd = fbi->fb.fix.smem_len; -+ -+ return 0; -+} -+ -+static int overlay1fb_enable(struct fb_info *info) -+{ -+ struct overlayfb_info *fbi = (struct overlayfb_info*) info; -+ unsigned long bpp1; -+ -+ if (!fbi->map_cpu) return -EINVAL; -+ -+ switch (fbi->fb.var.bits_per_pixel) { -+ case 16: -+ bpp1 = 0x4; -+ break; -+ case 18: -+ bpp1 = 0x6; -+ break; -+ case 19: -+ bpp1 = 0x8; -+ break; -+ case 24: -+ bpp1 = 0x9; -+ break; -+ case 25: -+ bpp1 = 0xa; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ /* disable branch/start/end of frame interrupt */ -+ LCCR5 |= (LCCR5_IUM1 | LCCR5_BSM1 | LCCR5_EOFM1 | LCCR5_SOFM1); -+ -+ if (fbi->state == C_DISABLE || fbi->state == C_BLANK) -+ FDADR1 = (fbi->dma1->fdadr); -+ else -+ FBR1 = fbi->dma1->fdadr | 0x1; -+ -+ /* enable overlay 1 window */ -+ OVL1C2 = (fbi->ypos << 10) | fbi->xpos; -+ OVL1C1 = OVL1C1_O1EN | (bpp1 << 20) | ((fbi->fb.var.yres-1)<<10) | (fbi->fb.var.xres-1); -+ -+ fbi->state = C_ENABLE; -+ -+ return 0; -+} -+ -+static int overlay1fb_disable(struct fb_info *info) -+{ -+ struct overlayfb_info *fbi = (struct overlayfb_info*)info; -+ int done; -+ -+ if ((fbi->state == C_DISABLE) || (fbi->state == C_BLANK)) -+ return 0; -+ -+ fbi->state = C_DISABLE; -+ -+ /* clear O1EN */ -+ OVL1C1 &= ~OVL1C1_O1EN; -+ -+ CLEAR_LCD_INTR(LCSR1, LCSR1_BS1); -+ FBR1 = 0x3; -+ done = WAIT_FOR_LCD_INTR(LCSR1, LCSR1_BS1, 100); -+ -+ if (!done) { -+ pr_debug(KERN_INFO "%s: timeout\n", __FUNCTION__); -+ return -1; -+ } -+ return 0; -+} -+ -+static int overlay1fb_blank(int blank, struct fb_info *info) -+{ -+ struct overlayfb_info *fbi = (struct overlayfb_info*) info; -+ int err=0; -+ -+ switch (blank) { -+ case 0: -+ err = overlay1fb_enable(info); -+ if (err) { -+ fbi->state = C_DISABLE; -+ pxafb_set_ctrlr_state(fbi->basefb, C_REENABLE); -+ } -+ break; -+ case 1: -+ err = overlay1fb_disable(info); -+ if (err) { -+ fbi->state = C_DISABLE; -+ pxafb_set_ctrlr_state(fbi->basefb, C_REENABLE); -+ } -+ break; -+ default: -+ break; -+ } -+ -+ return err; -+} -+ -+static int overlay1fb_check_var( struct fb_var_screeninfo *var, struct fb_info *info) -+{ -+ int xpos, ypos; -+ struct overlayfb_info *fbi=(struct overlayfb_info*)info; -+ -+ /* must in base frame */ -+ xpos = (var->nonstd & 0x3ff); -+ ypos = ((var->nonstd>>10) & 0x3ff); -+ -+ if ( (xpos + var->xres) > fbi->basefb->fb.var.xres ) -+ return -EINVAL; -+ -+ if ( (ypos + var->yres) > fbi->basefb->fb.var.yres ) -+ return -EINVAL; -+ -+ switch (var->bits_per_pixel) { -+ case 16: -+ if ( var->xres & 0x1 ) { -+ printk("xres should be a multiple of 2 pixels!\n"); -+ return -EINVAL; -+ } -+ break; -+ case 18: -+ case 19: -+ if ( var->xres & 0x7 ) { -+ printk("xres should be a multiple of 8 pixels!\n"); -+ return -EINVAL; -+ } -+ break; -+ default: -+ break; -+ } -+ -+ fbi->old_var=*var; -+ -+ var->activate=FB_ACTIVATE_NOW; -+ -+ return 0; -+} -+ -+ -+static int overlay1fb_set_par(struct fb_info *info) -+{ -+ int nbytes=0, err=0, pixels_per_line=0; -+ -+ struct overlayfb_info *fbi=(struct overlayfb_info*)info; -+ struct fb_var_screeninfo *var = &fbi->fb.var; -+ -+ info->flags &= ~FBINFO_MISC_USEREVENT; -+ -+ if (fbi->state == C_BLANK) -+ return 0; -+ -+ if (fbi->state == C_DISABLE) -+ goto out1; -+ -+ /* only xpos & ypos change */ -+ if ( (var->xres == fbi->old_var.xres) && -+ (var->yres == fbi->old_var.yres) && -+ (var->bits_per_pixel == fbi->old_var.bits_per_pixel) ) -+ goto out2; -+ -+out1: -+ switch(var->bits_per_pixel) { -+ case 16: -+ /* 2 pixels per line */ -+ pixels_per_line = (fbi->fb.var.xres + 0x1) & (~0x1); -+ nbytes = 2; -+ -+ var->red = def_rgbt_16.red; -+ var->green = def_rgbt_16.green; -+ var->blue = def_rgbt_16.blue; -+ var->transp = def_rgbt_16.transp; -+ -+ break; -+ case 18: -+ /* 8 pixels per line */ -+ pixels_per_line = (fbi->fb.var.xres + 0x7 ) & (~0x7); -+ nbytes = 3; -+ -+ var->red = def_rgb_18.red; -+ var->green = def_rgb_18.green; -+ var->blue = def_rgb_18.blue; -+ var->transp = def_rgb_18.transp; -+ -+ break; -+ case 19: -+ /* 8 pixels per line */ -+ pixels_per_line = (fbi->fb.var.xres + 0x7 ) & (~0x7); -+ nbytes = 3; -+ -+ var->red = def_rgbt_19.red; -+ var->green = def_rgbt_19.green; -+ var->blue = def_rgbt_19.blue; -+ var->transp = def_rgbt_19.transp; -+ -+ break; -+ case 24: -+ pixels_per_line = fbi->fb.var.xres; -+ nbytes = 4; -+ -+ var->red = def_rgbt_24.red; -+ var->green = def_rgbt_24.green; -+ var->blue = def_rgbt_24.blue; -+ var->transp = def_rgbt_24.transp; -+ -+ break; -+ case 25: -+ pixels_per_line = fbi->fb.var.xres; -+ nbytes = 4; -+ -+ var->red = def_rgbt_25.red; -+ var->green = def_rgbt_25.green; -+ var->blue = def_rgbt_25.blue; -+ var->transp = def_rgbt_25.transp; -+ -+ break; -+ } -+ -+ fbi->fb.fix.line_length = nbytes * pixels_per_line; -+ fbi->fb.fix.smem_len = fbi->fb.fix.line_length * fbi->fb.var.yres; -+ -+ err= overlay1fb_map_video_memory((struct fb_info*)fbi); -+ -+ if (err) -+ return err; -+ -+out2: -+ fbi->xpos = var->nonstd & 0x3ff; -+ fbi->ypos = (var->nonstd>>10) & 0x3ff; -+ -+ overlay1fb_enable(info); -+ -+ return 0; -+ -+} -+ -+static struct fb_ops overlay1fb_ops = { -+ .owner = THIS_MODULE, -+ .fb_open = overlay1fb_open, -+ .fb_release = overlay1fb_release, -+ .fb_check_var = overlay1fb_check_var, -+ .fb_set_par = overlay1fb_set_par, -+ .fb_blank = overlay1fb_blank, -+ .fb_fillrect = cfb_fillrect, -+ .fb_copyarea = cfb_copyarea, -+ .fb_imageblit = cfb_imageblit, -+}; -+ -+ /* -+ * LCD enhancement : Overlay 2 -+ * -+ * Features: -+ * - support planar YCbCr420/YCbCr422/YCbCr444; -+ */ -+static int overlay2fb_open(struct fb_info *info, int user) -+{ -+ struct overlayfb_info *fbi = (struct overlayfb_info*) info; -+ int ret = 0; -+ -+ /* if basefb is disable, enable fb. */ -+ if (fbi->basefb && fbi->basefb->state != C_ENABLE) -+ pxafb_blank(VESA_NO_BLANKING, (struct fb_info *)(fbi->basefb)); -+ -+ down(&fbi->mutex); -+ -+ if (fbi->refcount) -+ ret = -EACCES; -+ else -+ fbi->refcount ++; -+ -+ up(&fbi->mutex); -+ fbi->fb.var.xres = fbi->fb.var.yres = 0; -+ -+ return ret; -+} -+ -+static int overlay2fb_release(struct fb_info *info, int user) -+{ -+ struct overlayfb_info *fbi = (struct overlayfb_info*) info; -+ -+ down(&fbi->mutex); -+ -+ if (fbi->refcount) -+ fbi->refcount --; -+ -+ up(&fbi->mutex); -+ -+ /* disable overlay when released */ -+ overlay2fb_blank(1, info); -+ -+ return 0; -+} -+ -+static int overlay2fb_map_YUV_memory( struct fb_info *info) -+{ -+ struct overlayfb_info *fbi = (struct overlayfb_info*) info; -+ unsigned int ylen, cblen, crlen, aylen, acblen, acrlen; -+ unsigned int yoff, cboff, croff; -+ unsigned int xres,yres; -+ unsigned int nbytes; -+ -+ ylen = cblen = crlen = aylen = acblen = acrlen = 0; -+ yoff = cboff = croff = 0; -+ -+ if (fbi->map_cpu) -+ dma_free_writecombine(NULL, fbi->map_size, (void*)fbi->map_cpu, fbi->map_dma); -+ -+ yres = fbi->fb.var.yres; -+ -+ switch(fbi->format) { -+ case 0x4: /* YCbCr 4:2:0 planar */ -+ pr_debug("420 planar\n"); -+ /* 16 pixels per line */ -+ xres = (fbi->fb.var.xres + 0xf) & (~0xf); -+ fbi->fb.fix.line_length = xres; -+ -+ nbytes = xres * yres; -+ ylen = nbytes; -+ cblen = crlen = (nbytes/4); -+ -+ break; -+ case 0x3: /* YCbCr 4:2:2 planar */ -+ /* 8 pixles per line */ -+ pr_debug("422 planar\n"); -+ xres = (fbi->fb.var.xres + 0x7) & (~0x7); -+ fbi->fb.fix.line_length = xres; -+ -+ nbytes = xres * yres; -+ ylen = nbytes; -+ cblen = crlen = (nbytes/2); -+ -+ break; -+ case 0x2: /* YCbCr 4:4:4 planar */ -+ /* 4 pixels per line */ -+ pr_debug("444 planar\n"); -+ xres = (fbi->fb.var.xres + 0x3) & (~0x3); -+ fbi->fb.fix.line_length = xres; -+ -+ nbytes = xres * yres; -+ ylen = cblen = crlen = nbytes; -+ break; -+ } -+ -+ /* 16-bytes alignment for DMA */ -+ aylen = (ylen + 0xf) & (~0xf); -+ acblen = (cblen + 0xf) & (~0xf); -+ acrlen = (crlen + 0xf) & (~0xf); -+ -+ fbi->fb.fix.smem_len = aylen + acblen + acrlen; -+ -+ /* alloc memory */ -+ -+ fbi->map_size = PAGE_ALIGN(fbi->fb.fix.smem_len + PAGE_SIZE); -+ fbi->map_cpu = (unsigned long)dma_alloc_writecombine(NULL, fbi->map_size, -+ &fbi->map_dma, GFP_KERNEL ); -+ -+ if (!fbi->map_cpu) return -ENOMEM; -+ -+ fbi->screen_cpu = fbi->map_cpu + PAGE_SIZE; -+ fbi->screen_dma = fbi->map_dma + PAGE_SIZE; -+ -+ fbi->fb.fix.smem_start = fbi->screen_dma; -+ -+ /* setup dma for Planar format */ -+ fbi->dma2 = (struct pxafb_dma_descriptor*) -+ (fbi->screen_cpu - sizeof(struct pxafb_dma_descriptor)); -+ fbi->dma3 = fbi->dma2 - 1; -+ fbi->dma4 = fbi->dma3 - 1; -+ -+ /* offset */ -+ yoff = 0; -+ cboff = aylen; -+ croff = cboff + acblen; -+ -+ /* Y vector */ -+ fbi->dma2->fdadr = (fbi->screen_dma - sizeof(struct pxafb_dma_descriptor)); -+ fbi->dma2->fsadr = fbi->screen_dma + yoff; -+ fbi->dma2->fidr = 0; -+ fbi->dma2->ldcmd = ylen; -+ -+ /* Cb vector */ -+ fbi->dma3->fdadr = (fbi->dma2->fdadr - sizeof(struct pxafb_dma_descriptor)); -+ fbi->dma3->fsadr = (fbi->screen_dma + cboff); -+ fbi->dma3->fidr = 0; -+ fbi->dma3->ldcmd = cblen; -+ -+ /* Cr vector */ -+ -+ fbi->dma4->fdadr = (fbi->dma3->fdadr - sizeof(struct pxafb_dma_descriptor)); -+ fbi->dma4->fsadr = (fbi->screen_dma + croff); -+ fbi->dma4->fidr = 0; -+ fbi->dma4->ldcmd = crlen; -+ -+ /* adjust for user */ -+ fbi->fb.var.red.length = ylen; -+ fbi->fb.var.red.offset = yoff; -+ fbi->fb.var.green.length = cblen; -+ fbi->fb.var.green.offset = cboff; -+ fbi->fb.var.blue.length = crlen; -+ fbi->fb.var.blue.offset = croff; -+ -+ return 0; -+}; -+ -+static int overlay2fb_map_RGB_memory( struct fb_info *info) -+{ -+ struct overlayfb_info *fbi = (struct overlayfb_info*) info; -+ struct fb_var_screeninfo *var = &fbi->fb.var; -+ int pixels_per_line=0 , nbytes=0; -+ -+ if (fbi->map_cpu) -+ dma_free_writecombine(NULL, fbi->map_size, (void*)fbi->map_cpu, fbi->map_dma); -+ -+ switch(var->bits_per_pixel) { -+ case 16: -+ /* 2 pixels per line */ -+ pixels_per_line = (fbi->fb.var.xres + 0x1) & (~0x1); -+ nbytes = 2; -+ -+ var->red = def_rgbt_16.red; -+ var->green = def_rgbt_16.green; -+ var->blue = def_rgbt_16.blue; -+ var->transp = def_rgbt_16.transp; -+ break; -+ -+ case 18: -+ /* 8 pixels per line */ -+ pixels_per_line = (fbi->fb.var.xres + 0x7 ) & (~0x7); -+ nbytes = 3; -+ -+ var->red = def_rgb_18.red; -+ var->green = def_rgb_18.green; -+ var->blue = def_rgb_18.blue; -+ var->transp = def_rgb_18.transp; -+ -+ break; -+ case 19: -+ /* 8 pixels per line */ -+ pixels_per_line = (fbi->fb.var.xres + 0x7 ) & (~0x7); -+ nbytes = 3; -+ -+ var->red = def_rgbt_19.red; -+ var->green = def_rgbt_19.green; -+ var->blue = def_rgbt_19.blue; -+ var->transp = def_rgbt_19.transp; -+ -+ break; -+ case 24: -+ pixels_per_line = fbi->fb.var.xres; -+ nbytes = 4; -+ -+ var->red = def_rgbt_24.red; -+ var->green = def_rgbt_24.green; -+ var->blue = def_rgbt_24.blue; -+ var->transp = def_rgbt_24.transp; -+ -+ break; -+ -+ case 25: -+ pixels_per_line = fbi->fb.var.xres; -+ nbytes = 4; -+ -+ var->red = def_rgbt_25.red; -+ var->green = def_rgbt_25.green; -+ var->blue = def_rgbt_25.blue; -+ var->transp = def_rgbt_25.transp; -+ -+ break; -+ } -+ -+ fbi->fb.fix.line_length = nbytes * pixels_per_line; -+ fbi->fb.fix.smem_len = fbi->fb.fix.line_length * fbi->fb.var.yres; -+ -+ fbi->map_size = PAGE_ALIGN(fbi->fb.fix.smem_len + PAGE_SIZE); -+ fbi->map_cpu = (unsigned long)dma_alloc_writecombine(NULL, fbi->map_size, -+ &fbi->map_dma, GFP_KERNEL ); -+ -+ if (!fbi->map_cpu) return -ENOMEM; -+ -+ fbi->screen_cpu = fbi->map_cpu + PAGE_SIZE; -+ fbi->screen_dma = fbi->map_dma + PAGE_SIZE; -+ -+ fbi->fb.fix.smem_start = fbi->screen_dma; -+ -+ /* setup dma descriptor */ -+ fbi->dma2 = (struct pxafb_dma_descriptor*) -+ (fbi->screen_cpu - sizeof(struct pxafb_dma_descriptor)); -+ -+ fbi->dma2->fdadr = (fbi->screen_dma - sizeof(struct pxafb_dma_descriptor)); -+ fbi->dma2->fsadr = fbi->screen_dma; -+ fbi->dma2->fidr = 0; -+ fbi->dma2->ldcmd = fbi->fb.fix.smem_len; -+ -+ return 0; -+} -+ -+static int overlay2fb_enable(struct fb_info *info) -+{ -+ struct overlayfb_info *fbi = (struct overlayfb_info*) info; -+ unsigned long bpp2; -+ unsigned int xres, yres; -+ -+ if (!fbi->map_cpu) return -EINVAL; -+ -+ switch(fbi->fb.var.bits_per_pixel) { -+ case 16: -+ bpp2 = 0x4; -+ break; -+ case 18: -+ bpp2 = 0x6; -+ break; -+ case 19: -+ bpp2 = 0x8; -+ break; -+ case 24: -+ bpp2 = 0x9; -+ break; -+ case 25: -+ bpp2 = 0xa; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ /* disable branch/start/end of frame interrupt */ -+ LCCR5 |= (LCCR5_IUM4 | LCCR5_IUM3 | LCCR5_IUM2 | -+ LCCR5_BSM4 | LCCR5_BSM3 | LCCR5_BSM2 | -+ LCCR5_EOFM4 | LCCR5_EOFM3 | LCCR5_EOFM2 | -+ LCCR5_SOFM4 | LCCR5_SOFM3 | LCCR5_SOFM2); -+ -+ if (fbi->format == 0) { -+ /* overlay2 RGB resolution, RGB and YUV have different xres value*/ -+ xres = fbi->fb.var.xres; -+ yres = fbi->fb.var.yres; -+ -+ OVL2C2 = (fbi->format << 20) | (fbi->ypos << 10) | fbi->xpos; -+ OVL2C1 = OVL2C1_O2EN | (bpp2 << 20) | ((yres-1)<<10) | (xres-1); -+ /* setup RGB DMA */ -+ if (fbi->state == C_DISABLE || fbi->state == C_BLANK) -+ FDADR2 = fbi->dma2->fdadr; -+ else -+ FBR2 = fbi->dma2->fdadr | 0x1; -+ } else { -+ /* overlay2 YUV resolution */ -+ xres = fbi->fb.fix.line_length; -+ yres = fbi->fb.var.yres; -+ -+ OVL2C2 = (fbi->format << 20) | (fbi->ypos << 10) | fbi->xpos; -+ OVL2C1 = OVL2C1_O2EN | (bpp2 << 20) | ((yres-1)<<10) | (xres-1); -+ -+ if (fbi->state == C_DISABLE || fbi->state == C_BLANK) { -+ FDADR2 = fbi->dma2->fdadr; -+ FDADR3 = fbi->dma3->fdadr; -+ FDADR4 = fbi->dma4->fdadr; -+ } else { -+ FBR2 = fbi->dma2->fdadr | 0x01; -+ FBR3 = fbi->dma3->fdadr | 0x01; -+ FBR4 = fbi->dma4->fdadr | 0x01; -+ } -+ } -+ -+ fbi->state = C_ENABLE; -+ return 0; -+} -+ -+static int overlay2fb_disable(struct fb_info *info) -+{ -+ struct overlayfb_info *fbi = (struct overlayfb_info*)info; -+ int done; -+ -+ if (fbi->state == C_DISABLE) -+ return 0; -+ if (fbi->state == C_BLANK) { -+ fbi->state = C_DISABLE; -+ return 0; -+ } -+ -+ fbi->state = C_DISABLE; -+ -+ /* clear O2EN */ -+ OVL2C1 &= ~OVL2C1_O2EN; -+ -+ /* Make overlay2 can't disable/enable -+ * correctly sometimes. -+ */ -+ CLEAR_LCD_INTR(LCSR1, LCSR1_BS2); -+ -+ if (fbi->format == 0) -+ FBR2 = 0x3; -+ else { -+ FBR2 = 0x3; -+ FBR3 = 0x3; -+ FBR4 = 0x3; -+ } -+ -+ done = WAIT_FOR_LCD_INTR(LCSR1, LCSR1_BS2, 100); -+ -+ if (!done) { -+ pr_debug(KERN_INFO "%s: timeout\n", __FUNCTION__); -+ return -1; -+ } -+ return 0; -+} -+ -+static int overlay2fb_blank(int blank, struct fb_info *info) -+{ -+ struct overlayfb_info *fbi = (struct overlayfb_info*) info; -+ int err=0; -+ -+ switch(blank) -+ { -+ case 0: -+ err = overlay2fb_enable(info); -+ if (err) { -+ fbi->state = C_DISABLE; -+ pxafb_set_ctrlr_state(fbi->basefb, C_REENABLE); -+ } -+ break; -+ case 1: -+ err = overlay2fb_disable(info); -+ if (err) { -+ fbi->state = C_DISABLE; -+ pxafb_set_ctrlr_state(fbi->basefb, C_REENABLE); -+ } -+ break; -+ default: -+ /* reserved */ -+ break; -+ } -+ -+ return err; -+} -+ -+ -+static int overlay2fb_check_var( struct fb_var_screeninfo *var, struct fb_info *info) -+{ -+ int xpos, ypos, xres, yres; -+ int format; -+ struct overlayfb_info *fbi=(struct overlayfb_info*)info; -+ -+ xres=yres=0; -+ -+ xpos = (var->nonstd & 0x3ff); -+ ypos = (var->nonstd >> 10) & 0x3ff; -+ format = (var->nonstd >>20) & 0x7; -+ -+ -+ /* Palnar YCbCr444, YCbCr422, YCbCr420 */ -+ if ( (format != 0x4) && (format != 0x3) && (format != 0x2) && (format !=0x0)) -+ return -EINVAL; -+ -+ /* dummy pixels */ -+ switch(format) { -+ case 0x0: /* RGB */ -+ xres = var->xres; -+ break; -+ case 0x2: /* 444 */ -+ xres = (var->xres + 0x3) & ~(0x3); -+ break; -+ case 0x3: /* 422 */ -+ xres = (var->xres + 0x7) & ~(0x7); -+ break; -+ case 0x4: /* 420 */ -+ xres = (var->xres + 0xf) & ~(0xf); -+ break; -+ } -+ yres = var->yres; -+ -+ if ( (xpos + xres) > fbi->basefb->fb.var.xres ) -+ return -EINVAL; -+ -+ if ( (ypos + yres) > fbi->basefb->fb.var.yres ) -+ return -EINVAL; -+ -+ fbi->old_var=*var; -+ -+ var->activate=FB_ACTIVATE_NOW; -+ -+ return 0; -+ -+} -+ -+ -+/* -+ * overlay2fb_set_var() -+ * -+ * var.nonstd is used as YCbCr format. -+ * var.red/green/blue is used as (Y/Cb/Cr) vector -+ */ -+ -+static int overlay2fb_set_par(struct fb_info *info) -+{ -+ unsigned int xpos, ypos; -+ int format, err; -+ -+ struct overlayfb_info *fbi=(struct overlayfb_info*)info; -+ struct fb_var_screeninfo *var = &fbi->fb.var; -+ -+ info->flags &= ~FBINFO_MISC_USEREVENT; -+ -+ if (fbi->state == C_BLANK) -+ return 0; -+ -+ if (fbi->state == C_DISABLE) -+ goto out1; -+ -+ if ( (var->xres == fbi->old_var.xres) && -+ (var->yres == fbi->old_var.yres) && -+ (var->bits_per_pixel == fbi->old_var.bits_per_pixel) && -+ (((var->nonstd>>20) & 0x7) == fbi->format) ) -+ goto out2; -+ -+out1: -+ xpos = var->nonstd & 0x3ff; -+ ypos = (var->nonstd>>10) & 0x3ff; -+ format = (var->nonstd>>20) & 0x7; -+ -+ -+ fbi->format = format; -+ if ( fbi->format==0 ) -+ err = overlay2fb_map_RGB_memory(info); -+ else -+ err = overlay2fb_map_YUV_memory(info); -+ -+ if (err) return err; -+ -+out2: -+ /* position */ -+ fbi->xpos = var->nonstd & 0x3ff; -+ fbi->ypos = (var->nonstd>>10) & 0x3ff; -+ -+ overlay2fb_enable(info); -+ -+ return 0; -+} -+ -+static struct fb_ops overlay2fb_ops = { -+ .owner = THIS_MODULE, -+ .fb_open = overlay2fb_open, -+ .fb_release = overlay2fb_release, -+ .fb_check_var = overlay2fb_check_var, -+ .fb_set_par = overlay2fb_set_par, -+ .fb_blank = overlay2fb_blank, -+ .fb_fillrect = cfb_fillrect, -+ .fb_copyarea = cfb_copyarea, -+ .fb_imageblit = cfb_imageblit, -+}; -+ -+/* Hardware cursor */ -+ -+/* Bulverde Cursor Modes */ -+struct cursorfb_mode{ -+ int xres; -+ int yres; -+ int bpp; -+}; -+ -+static struct cursorfb_mode cursorfb_modes[]={ -+ { 32, 32, 2}, -+ { 32, 32, 2}, -+ { 32, 32, 2}, -+ { 64, 64, 2}, -+ { 64, 64, 2}, -+ { 64, 64, 2}, -+ {128, 128, 1}, -+ {128, 128, 1} -+}; -+ -+static int cursorfb_enable(struct fb_info *info) -+{ -+ struct overlayfb_info *fbi = (struct overlayfb_info*) info; -+ -+ if (!fbi->map_cpu) return -EINVAL; -+ -+ CCR &= ~CCR_CEN; -+ -+ /* set palette format -+ * -+ * FIXME: if only cursor uses palette -+ */ -+ LCCR4 = (LCCR4 & (~(0x3<<15))) | (0x1<<15); -+ -+ /* disable branch/start/end of frame interrupt */ -+ LCCR5 |= (LCCR5_IUM5 | LCCR5_BSM5 | LCCR5_EOFM5 | LCCR5_SOFM5); -+ -+ /* load palette and frame data */ -+ if (fbi->state == C_DISABLE) { -+ FDADR5 = fbi->dma5_pal->fdadr; -+ udelay(1); -+ FDADR5 = fbi->dma5_frame->fdadr; -+ udelay(1); -+ -+ } -+ else { -+ FBR5 = fbi->dma5_pal->fdadr | 0x1; -+ udelay(1); -+ FBR5 = fbi->dma5_frame->fdadr | 0x1; -+ udelay(1); -+ } -+ -+ CCR = CCR_CEN | (fbi->ypos << 15) | (fbi->xpos << 5) | (fbi->format); -+ -+ fbi->state = C_ENABLE; -+ -+ return 0; -+} -+ -+static int cursorfb_disable(struct fb_info *info) -+{ -+ struct overlayfb_info *fbi = (struct overlayfb_info*)info; -+ int done, ret = 0; -+ -+ fbi->state = C_DISABLE; -+ -+ done = WAIT_FOR_LCD_INTR(LCSR1, LCSR1_BS5, 100); -+ if (!done) ret = -1; -+ -+ CCR &= ~CCR_CEN; -+ -+ return ret; -+} -+ -+static int cursorfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, -+ u_int trans, struct fb_info *info) -+{ -+ struct overlayfb_info *fbi = (struct overlayfb_info *)info; -+ u_int val, ret = 1; -+ u_int *pal=(u_int*) fbi->palette_cpu; -+ -+ /* 25bit with Transparcy for 16bpp format */ -+ if (regno < fbi->palette_size) { -+ val = ((trans << 24) & 0x1000000); -+ val |= ((red << 16) & 0x0ff0000); -+ val |= ((green << 8 ) & 0x000ff00); -+ val |= ((blue << 0) & 0x00000ff); -+ -+ pal[regno] = val; -+ ret = 0; -+ } -+ return ret; -+} -+ -+int cursorfb_blank(int blank, struct fb_info *info) -+{ -+ switch(blank) -+ { -+ case 0: -+ cursorfb_enable(info); -+ break; -+ case 1: -+ cursorfb_disable(info); -+ break; -+ default: -+ /* reserved */ -+ break; -+ } -+ return 0; -+} -+ -+static int cursorfb_check_var( struct fb_var_screeninfo *var, struct fb_info *info) -+{ -+ int xpos, ypos, xres, yres; -+ int mode; -+ struct cursorfb_mode *cursor; -+ struct overlayfb_info *fbi=(struct overlayfb_info*)info; -+ -+ mode = var->nonstd & 0x7; -+ xpos = (var->nonstd>>5) & 0x3ff; -+ ypos = (var->nonstd>>15) & 0x3ff; -+ -+ if (mode>7 || mode <0 ) -+ return -EINVAL; -+ -+ cursor = cursorfb_modes + mode; -+ -+ xres = cursor->xres; -+ yres = cursor->yres; -+ -+ if ( (xpos + xres) > fbi->basefb->fb.var.xres ) -+ return -EINVAL; -+ -+ if ( (ypos + yres) > fbi->basefb->fb.var.yres ) -+ return -EINVAL; -+ -+ return 0; -+ -+} -+ -+static int cursorfb_set_par(struct fb_info *info) -+{ -+ struct overlayfb_info *fbi = (struct overlayfb_info*) info; -+ struct fb_var_screeninfo *var = &fbi->fb.var; -+ struct cursorfb_mode *cursor; -+ int mode, xpos, ypos; -+ int err; -+ -+ info->flags &= ~FBINFO_MISC_USEREVENT; -+ -+ mode = var->nonstd & 0x7; -+ xpos = (var->nonstd>>5) & 0x3ff; -+ ypos = (var->nonstd>>15) & 0x3ff; -+ -+ if (mode != fbi->format) { -+ cursor = cursorfb_modes + mode; -+ -+ /* update "var" info */ -+ fbi->fb.var.xres = cursor->xres; -+ fbi->fb.var.yres = cursor->yres; -+ fbi->fb.var.bits_per_pixel = cursor->bpp; -+ -+ /* alloc video memory -+ * -+ * 4k is engouh for 128x128x1 cursor, -+ * - 2k for cursor pixels, -+ * - 2k for palette data, plus 2 dma descriptor -+ */ -+ if (!fbi->map_cpu) { -+ fbi->map_size = PAGE_SIZE; -+ fbi->map_cpu = (unsigned long)dma_alloc_writecombine(NULL, fbi->map_size, -+ &fbi->map_dma, GFP_KERNEL ); -+ if (!fbi->map_cpu) return -ENOMEM; -+ } -+ -+ cursor = cursorfb_modes + mode; -+ -+ /* update overlay & fix "info" */ -+ fbi->screen_cpu = fbi->map_cpu; -+ fbi->palette_cpu = fbi->map_cpu + (PAGE_SIZE/2); -+ fbi->screen_dma = fbi->map_dma; -+ fbi->palette_dma = fbi->map_dma + (PAGE_SIZE/2); -+ -+ fbi->format = mode; -+ fbi->palette_size = (1<<cursor->bpp); -+ fbi->fb.fix.smem_start = fbi->screen_dma; -+ fbi->fb.fix.smem_len = cursor->xres * cursor->yres * cursor->bpp / 8; -+ fbi->fb.fix.line_length = cursor->xres * cursor->bpp / 8; -+ -+ fbi->dma5_pal = (struct pxafb_dma_descriptor*)(fbi->map_cpu + PAGE_SIZE - 16 ); -+ fbi->dma5_pal->fdadr = (fbi->map_dma + PAGE_SIZE - 16); -+ fbi->dma5_pal->fsadr = fbi->palette_dma; -+ fbi->dma5_pal->fidr = 0; -+ fbi->dma5_pal->ldcmd = (fbi->palette_size<<2) | LDCMD_PAL; -+ -+ fbi->dma5_frame = (struct pxafb_dma_descriptor*)(fbi->map_cpu + PAGE_SIZE - 32 ); -+ fbi->dma5_frame->fdadr = (fbi->map_dma + PAGE_SIZE - 32); -+ fbi->dma5_frame->fsadr = fbi->screen_dma; -+ fbi->dma5_frame->fidr = 0; -+ fbi->dma5_frame->ldcmd = fbi->fb.fix.smem_len; -+ -+ /* alloc & set default cmap */ -+ err = fb_alloc_cmap(&fbi->fb.cmap, fbi->palette_size, 0); -+ if (err) return err; -+ err = fb_set_cmap(&fbi->fb.cmap, info); -+ if (err) return err; -+ } -+ -+ /* update overlay info */ -+ if ( (xpos != fbi->xpos) || (ypos != fbi->ypos) ) { -+ fbi->xpos = xpos; -+ fbi->ypos = ypos; -+ } -+ -+ cursorfb_enable(info); -+ pxafb_set_ctrlr_state(fbi->basefb, C_REENABLE); -+ -+ return 0; -+} -+ -+static struct fb_ops cursorfb_ops = { -+ .owner = THIS_MODULE, -+ .fb_check_var = cursorfb_check_var, -+ .fb_set_par = cursorfb_set_par, -+ .fb_blank = cursorfb_blank, -+ .fb_fillrect = cfb_fillrect, -+ .fb_copyarea = cfb_copyarea, -+ .fb_imageblit = cfb_imageblit, -+ .fb_setcolreg = cursorfb_setcolreg, -+}; -+ -+static struct overlayfb_info * __init overlay1fb_init_fbinfo(void) -+{ -+ struct overlayfb_info *fbi; -+ -+ fbi = kmalloc(sizeof(struct overlayfb_info) + sizeof(u16) * 16, GFP_KERNEL); -+ if (!fbi) -+ return NULL; -+ -+ memset(fbi, 0, sizeof(struct overlayfb_info) ); -+ -+ fbi->refcount = 0; -+ init_MUTEX(&fbi->mutex); -+ -+ strcpy(fbi->fb.fix.id, "overlay1"); -+ -+ fbi->fb.fix.type = FB_TYPE_PACKED_PIXELS; -+ fbi->fb.fix.type_aux = 0; -+ fbi->fb.fix.xpanstep = 0; -+ fbi->fb.fix.ypanstep = 0; -+ fbi->fb.fix.ywrapstep = 0; -+ fbi->fb.fix.accel = FB_ACCEL_NONE; -+ -+ fbi->fb.var.nonstd = 0; -+ fbi->fb.var.activate = FB_ACTIVATE_NOW; -+ fbi->fb.var.height = -1; -+ fbi->fb.var.width = -1; -+ fbi->fb.var.accel_flags = 0; -+ fbi->fb.var.vmode = FB_VMODE_NONINTERLACED; -+ -+ -+ fbi->fb.fbops = &overlay1fb_ops; -+ fbi->fb.flags = FBINFO_FLAG_DEFAULT; -+ fbi->fb.node = -1; -+ fbi->fb.pseudo_palette = NULL; -+ -+ fbi->xpos = 0; -+ fbi->ypos = 0; -+ fbi->format = -1; -+ fbi->state = C_DISABLE; -+ -+ return fbi; -+} -+ -+static struct overlayfb_info * __init overlay2fb_init_fbinfo(void) -+{ -+ struct overlayfb_info *fbi; -+ -+ fbi = kmalloc(sizeof(struct overlayfb_info) + sizeof(u16) * 16, GFP_KERNEL); -+ if (!fbi) -+ return NULL; -+ -+ memset(fbi, 0, sizeof(struct overlayfb_info) ); -+ -+ fbi->refcount = 0; -+ init_MUTEX(&fbi->mutex); -+ -+ strcpy(fbi->fb.fix.id, "overlay2"); -+ -+ fbi->fb.fix.type = FB_TYPE_PACKED_PIXELS; -+ fbi->fb.fix.type_aux = 0; -+ fbi->fb.fix.xpanstep = 0; -+ fbi->fb.fix.ypanstep = 0; -+ fbi->fb.fix.ywrapstep = 0; -+ fbi->fb.fix.accel = FB_ACCEL_NONE; -+ -+ fbi->fb.var.nonstd = 0; -+ fbi->fb.var.activate = FB_ACTIVATE_NOW; -+ fbi->fb.var.height = -1; -+ fbi->fb.var.width = -1; -+ fbi->fb.var.accel_flags = 0; -+ fbi->fb.var.vmode = FB_VMODE_NONINTERLACED; -+ -+ fbi->fb.fbops = &overlay2fb_ops; -+ fbi->fb.flags = FBINFO_FLAG_DEFAULT; -+ fbi->fb.node = -1; -+ fbi->fb.pseudo_palette = NULL; -+ -+ fbi->xpos = 0; -+ fbi->ypos = 0; -+ fbi->format = -1; -+ fbi->state = C_DISABLE; -+ -+ return fbi; -+} -+ -+static struct overlayfb_info * __init cursorfb_init_fbinfo(void) -+{ -+ struct overlayfb_info *fbi; -+ -+ fbi = kmalloc(sizeof(struct overlayfb_info) + sizeof(u16) * 16, GFP_KERNEL); -+ if (!fbi) -+ return NULL; -+ -+ memset(fbi, 0, sizeof(struct overlayfb_info) ); -+ -+ fbi->refcount = 0; -+ init_MUTEX(&fbi->mutex); -+ -+ strcpy(fbi->fb.fix.id, "cursor"); -+ -+ fbi->fb.fix.type = FB_TYPE_PACKED_PIXELS; -+ fbi->fb.fix.type_aux = 0; -+ fbi->fb.fix.xpanstep = 0; -+ fbi->fb.fix.ypanstep = 0; -+ fbi->fb.fix.ywrapstep = 0; -+ fbi->fb.fix.accel = FB_ACCEL_NONE; -+ -+ fbi->fb.var.nonstd = 0; -+ fbi->fb.var.activate = FB_ACTIVATE_NOW; -+ fbi->fb.var.height = -1; -+ fbi->fb.var.width = -1; -+ fbi->fb.var.accel_flags = 0; -+ fbi->fb.var.vmode = FB_VMODE_NONINTERLACED; -+ -+ fbi->fb.fbops = &cursorfb_ops; -+ fbi->fb.flags = FBINFO_FLAG_DEFAULT; -+ fbi->fb.node = -1; -+ fbi->fb.pseudo_palette = NULL; -+ -+ -+ fbi->xpos = 0; -+ fbi->ypos = 0; -+ fbi->format = -1; -+ fbi->state = C_DISABLE; -+ -+ return fbi; -+} -+ -+ -+void pxa_set_overlay_ctrlr_state(struct pxafb_info *fbi, u_int state) -+{ -+ switch (state) { -+ case C_DISABLE: -+ DISABLE_OVERLAYS(fbi); -+ break; -+ case C_ENABLE: -+ ENABLE_OVERLAYS(fbi); -+ break; -+ case C_BLANK: -+ BLANK_OVERLAYS(fbi); -+ break; -+ case C_UNBLANK: -+ UNBLANK_OVERLAYS(fbi); -+ break; -+ default: -+ break; -+ } -+} -+ -+static int is_pxafb_device(struct device * dev, void * data) -+{ -+ struct platform_device *pdev = container_of(dev, struct platform_device, dev); -+ -+ return (strncmp(pdev->name, "pxa2xx-fb", 9) == 0); -+} -+ -+static int __devinit pxafb_overlay_init(void) -+{ -+ int ret; -+ struct overlayfb_info *overlay1fb, *overlay2fb, *cursorfb; -+ struct pxafb_info *fbi; -+ struct device *dev; -+ -+ ret = -1; -+ overlay1fb = overlay2fb = cursorfb = NULL; -+ fbi = NULL; -+ -+ dev = bus_find_device(&platform_bus_type, NULL, NULL, is_pxafb_device); -+ if (!dev) { -+ printk(KERN_INFO "Base framebuffer not exists, failed to load overlay driver!\n"); -+ return ret; -+ } -+ -+ fbi = dev_get_drvdata(dev); -+ if (fbi == NULL) { -+ printk(KERN_INFO "Base framebuffer not initialized, failed to load overlay driver!\n"); -+ return ret; -+ } -+ -+ /* Overlay 1 windows */ -+ overlay1fb = overlay1fb_init_fbinfo(); -+ -+ if (!overlay1fb) { -+ ret = -ENOMEM; -+ printk("overlay1fb_init_fbinfo failed\n"); -+ goto failed; -+ } -+ -+ ret = register_framebuffer(&overlay1fb->fb); -+ if (ret < 0) -+ goto failed; -+ -+ /* Overlay 2 window */ -+ overlay2fb = overlay2fb_init_fbinfo(); -+ -+ if (!overlay2fb) { -+ ret = -ENOMEM; -+ printk("overlay2fb_init_fbinfo failed\n"); -+ goto failed; -+ } -+ -+ ret = register_framebuffer(&overlay2fb->fb); -+ if (ret < 0) goto failed; -+ -+ /* Hardware cursor window */ -+ cursorfb = cursorfb_init_fbinfo(); -+ -+ if (!cursorfb) { -+ ret = -ENOMEM; -+ printk("cursorfb_init_fbinfo failed\n"); -+ goto failed; -+ } -+ -+ ret = register_framebuffer(&cursorfb->fb); -+ if (ret < 0) goto failed; -+ -+ -+ /* set refernce to Overlays */ -+ fbi->overlay1fb = overlay1fb; -+ fbi->overlay2fb = overlay2fb; -+ fbi->cursorfb = cursorfb; -+ fbi->set_overlay_ctrlr_state=pxa_set_overlay_ctrlr_state; -+ -+ /* set refernce to BaseFrame */ -+ overlay1fb->basefb = fbi; -+ overlay2fb->basefb = fbi; -+ cursorfb->basefb = fbi; -+ -+ printk(KERN_INFO "Load PXA Overlay driver successfully!\n"); -+ -+ return 0; -+ -+failed: -+ if (overlay1fb) -+ kfree(overlay1fb); -+ if (overlay2fb) -+ kfree(overlay2fb); -+ if (cursorfb) -+ kfree(cursorfb); -+ printk(KERN_INFO "Load PXA Overlay driver failed!\n"); -+ return ret; -+} -+ -+static void __exit pxafb_overlay_exit(void) -+{ -+ struct pxafb_info *fbi; -+ struct device *dev; -+ -+ dev = bus_find_device(&platform_bus_type, NULL, NULL, is_pxafb_device); -+ if (!dev) -+ return; -+ -+ fbi = dev_get_drvdata(dev); -+ if (!fbi) -+ return; -+ -+ if (fbi->overlay1fb) { -+ unregister_framebuffer(&(fbi->overlay1fb->fb)); -+ kfree(fbi->overlay1fb); -+ fbi->overlay1fb = NULL; -+ } -+ -+ if (fbi->overlay2fb) { -+ unregister_framebuffer(&(fbi->overlay2fb->fb)); -+ kfree(fbi->overlay2fb); -+ fbi->overlay2fb = NULL; -+ } -+ -+ if (fbi->cursorfb) { -+ unregister_framebuffer(&(fbi->cursorfb->fb)); -+ kfree(fbi->cursorfb); -+ fbi->cursorfb = NULL; -+ } -+ -+ fbi->set_overlay_ctrlr_state = NULL; -+ -+ printk(KERN_INFO "Unload PXA Overlay driver successfully!\n"); -+ return; -+} -+ -+ -+module_init(pxafb_overlay_init); -+module_exit(pxafb_overlay_exit); -+ -+MODULE_DESCRIPTION("Loadable framebuffer overlay driver for PXA"); -+MODULE_LICENSE("GPL"); -+ ---- linux-2.6.24-rc1.orig/include/asm-arm/arch-pxa/pxa-regs.h -+++ linux-2.6.24-rc1/include/asm-arm/arch-pxa/pxa-regs.h -@@ -789,11 +789,18 @@ - #define UDC_INT_PACKETCMP (0x1) - - #define UDCICR_INT(n,intr) (((intr) & 0x03) << (((n) & 0x0F) * 2)) -+/* Older defines, do not use. */ - #define UDCICR1_IECC (1 << 31) /* IntEn - Configuration Change */ - #define UDCICR1_IESOF (1 << 30) /* IntEn - Start of Frame */ - #define UDCICR1_IERU (1 << 29) /* IntEn - Resume */ - #define UDCICR1_IESU (1 << 28) /* IntEn - Suspend */ - #define UDCICR1_IERS (1 << 27) /* IntEn - Reset */ -+/* New defines. */ -+#define UDCISR1_IRCC (1 << 31) /* IntEn - Configuration Change */ -+#define UDCISR1_IRSOF (1 << 30) /* IntEn - Start of Frame */ -+#define UDCISR1_IRRU (1 << 29) /* IntEn - Resume */ -+#define UDCISR1_IRSU (1 << 28) /* IntEn - Suspend */ -+#define UDCISR1_IRRS (1 << 27) /* IntEn - Reset */ - - #define UDCISR0 __REG(0x4060000C) /* UDC Interrupt Status Register 0 */ - #define UDCISR1 __REG(0x40600010) /* UDC Interrupt Status Register 1 */ -@@ -1827,6 +1834,8 @@ - #define DFBR0 __REG(0x44000020) /* DMA Channel 0 Frame Branch Register */ - #define DFBR1 __REG(0x44000024) /* DMA Channel 1 Frame Branch Register */ - #define LCSR __REG(0x44000038) /* LCD Controller Status Register */ -+#define LCSR0 __REG(0x44000038) /* LCD Controller Status Register */ -+#define LCSR1 __REG(0x44000034) /* LCD Controller Status Register */ - #define LIIDR __REG(0x4400003C) /* LCD Controller Interrupt ID Register */ - #define TMEDRGBR __REG(0x44000040) /* TMED RGB Seed Register */ - #define TMEDCR __REG(0x44000044) /* TMED Control Register */ -@@ -1836,6 +1845,10 @@ - #define LCCR3_4BPP (2 << 24) - #define LCCR3_8BPP (3 << 24) - #define LCCR3_16BPP (4 << 24) -+#define LCCR3_18BPP (6 << 24) -+#define LCCR3_19BPP (8 << 24) -+#define LCCR3_24BPP (9 << 24) -+#define LCCR3_25BPP (10<< 24) - - #define LCCR3_PDFOR_0 (0 << 30) - #define LCCR3_PDFOR_1 (1 << 30) -@@ -2010,6 +2023,104 @@ - - #define LDCMD_PAL (1 << 26) /* instructs DMA to load palette buffer */ - -+/* Overlay1 & Overlay2 & Hardware Cursor */ -+#define LCSR1_SOF1 (1 << 0) -+#define LCSR1_SOF2 (1 << 1) -+#define LCSR1_SOF3 (1 << 2) -+#define LCSR1_SOF4 (1 << 3) -+#define LCSR1_SOF5 (1 << 4) -+#define LCSR1_SOF6 (1 << 5) -+ -+#define LCSR1_EOF1 (1 << 8) -+#define LCSR1_EOF2 (1 << 9) -+#define LCSR1_EOF3 (1 << 10) -+#define LCSR1_EOF4 (1 << 11) -+#define LCSR1_EOF5 (1 << 12) -+#define LCSR1_EOF6 (1 << 13) -+ -+#define LCSR1_BS1 (1 << 16) -+#define LCSR1_BS2 (1 << 17) -+#define LCSR1_BS3 (1 << 18) -+#define LCSR1_BS4 (1 << 19) -+#define LCSR1_BS5 (1 << 20) -+#define LCSR1_BS6 (1 << 21) -+ -+#define LCSR1_IU2 (1 << 25) -+#define LCSR1_IU3 (1 << 26) -+#define LCSR1_IU4 (1 << 27) -+#define LCSR1_IU5 (1 << 28) -+#define LCSR1_IU6 (1 << 29) -+ -+#define LDCMD_SOFINT (1 << 22) -+#define LDCMD_EOFINT (1 << 21) -+ -+ -+#define LCCR5_SOFM1 (1<<0) /* Start Of Frame Mask for Overlay 1 (channel 1) */ -+#define LCCR5_SOFM2 (1<<1) /* Start Of Frame Mask for Overlay 2 (channel 2) */ -+#define LCCR5_SOFM3 (1<<2) /* Start Of Frame Mask for Overlay 2 (channel 3) */ -+#define LCCR5_SOFM4 (1<<3) /* Start Of Frame Mask for Overlay 2 (channel 4) */ -+#define LCCR5_SOFM5 (1<<4) /* Start Of Frame Mask for cursor (channel 5) */ -+#define LCCR5_SOFM6 (1<<5) /* Start Of Frame Mask for command data (channel 6) */ -+ -+#define LCCR5_EOFM1 (1<<8) /* End Of Frame Mask for Overlay 1 (channel 1) */ -+#define LCCR5_EOFM2 (1<<9) /* End Of Frame Mask for Overlay 2 (channel 2) */ -+#define LCCR5_EOFM3 (1<<10) /* End Of Frame Mask for Overlay 2 (channel 3) */ -+#define LCCR5_EOFM4 (1<<11) /* End Of Frame Mask for Overlay 2 (channel 4) */ -+#define LCCR5_EOFM5 (1<<12) /* End Of Frame Mask for cursor (channel 5) */ -+#define LCCR5_EOFM6 (1<<13) /* End Of Frame Mask for command data (channel 6) */ -+ -+#define LCCR5_BSM1 (1<<16) /* Branch mask for Overlay 1 (channel 1) */ -+#define LCCR5_BSM2 (1<<17) /* Branch mask for Overlay 2 (channel 2) */ -+#define LCCR5_BSM3 (1<<18) /* Branch mask for Overlay 2 (channel 3) */ -+#define LCCR5_BSM4 (1<<19) /* Branch mask for Overlay 2 (channel 4) */ -+#define LCCR5_BSM5 (1<<20) /* Branch mask for cursor (channel 5) */ -+#define LCCR5_BSM6 (1<<21) /* Branch mask for data command (channel 6) */ -+ -+#define LCCR5_IUM1 (1<<24) /* Input FIFO Underrun Mask for Overlay 1 */ -+#define LCCR5_IUM2 (1<<25) /* Input FIFO Underrun Mask for Overlay 2 */ -+#define LCCR5_IUM3 (1<<26) /* Input FIFO Underrun Mask for Overlay 2 */ -+#define LCCR5_IUM4 (1<<27) /* Input FIFO Underrun Mask for Overlay 2 */ -+#define LCCR5_IUM5 (1<<28) /* Input FIFO Underrun Mask for cursor */ -+#define LCCR5_IUM6 (1<<29) /* Input FIFO Underrun Mask for data command */ -+ -+#define OVL1C1_O1EN (1<<31) /* Enable bit for Overlay 1 */ -+#define OVL2C1_O2EN (1<<31) /* Enable bit for Overlay 2 */ -+#define CCR_CEN (1<<31) /* Enable bit for Cursor */ -+ -+/* LCD registers */ -+#define LCCR4 __REG(0x44000010) /* LCD Controller Control Register 4 */ -+#define LCCR5 __REG(0x44000014) /* LCD Controller Control Register 5 */ -+#define FBR0 __REG(0x44000020) /* DMA Channel 0 Frame Branch Register */ -+#define FBR1 __REG(0x44000024) /* DMA Channel 1 Frame Branch Register */ -+#define FBR2 __REG(0x44000028) /* DMA Channel 2 Frame Branch Register */ -+#define FBR3 __REG(0x4400002C) /* DMA Channel 3 Frame Branch Register */ -+#define FBR4 __REG(0x44000030) /* DMA Channel 4 Frame Branch Register */ -+#define FDADR2 __REG(0x44000220) /* DMA Channel 2 Frame Descriptor Address Register */ -+#define FSADR2 __REG(0x44000224) /* DMA Channel 2 Frame Source Address Register */ -+#define FIDR2 __REG(0x44000228) /* DMA Channel 2 Frame ID Register */ -+#define LDCMD2 __REG(0x4400022C) /* DMA Channel 2 Command Register */ -+#define FDADR3 __REG(0x44000230) /* DMA Channel 3 Frame Descriptor Address Register */ -+#define FSADR3 __REG(0x44000234) /* DMA Channel 3 Frame Source Address Register */ -+#define FIDR3 __REG(0x44000238) /* DMA Channel 3 Frame ID Register */ -+#define LDCMD3 __REG(0x4400023C) /* DMA Channel 3 Command Register */ -+#define FDADR4 __REG(0x44000240) /* DMA Channel 4 Frame Descriptor Address Register */ -+#define FSADR4 __REG(0x44000244) /* DMA Channel 4 Frame Source Address Register */ -+#define FIDR4 __REG(0x44000248) /* DMA Channel 4 Frame ID Register */ -+#define LDCMD4 __REG(0x4400024C) /* DMA Channel 4 Command Register */ -+#define FDADR5 __REG(0x44000250) /* DMA Channel 5 Frame Descriptor Address Register */ -+#define FSADR5 __REG(0x44000254) /* DMA Channel 5 Frame Source Address Register */ -+#define FIDR5 __REG(0x44000258) /* DMA Channel 5 Frame ID Register */ -+#define LDCMD5 __REG(0x4400025C) /* DMA Channel 5 Command Register */ -+ -+#define OVL1C1 __REG(0x44000050) /* Overlay 1 Control Register 1 */ -+#define OVL1C2 __REG(0x44000060) /* Overlay 1 Control Register 2 */ -+#define OVL2C1 __REG(0x44000070) /* Overlay 2 Control Register 1 */ -+#define OVL2C2 __REG(0x44000080) /* Overlay 2 Control Register 2 */ -+#define CCR __REG(0x44000090) /* Cursor Control Register */ -+ -+#define FBR5 __REG(0x44000110) /* DMA Channel 5 Frame Branch Register */ -+#define FBR6 __REG(0x44000114) /* DMA Channel 6 Frame Branch Register */ -+ - /* - * Memory controller - */ diff --git a/meta/packages/linux/linux-rp-2.6.23+2.6.24-rc8/squashfs3.0-2.6.15.patch b/meta/packages/linux/linux-rp-2.6.23+2.6.24-rc8/squashfs3.0-2.6.15.patch deleted file mode 100644 index a210afcaf8..0000000000 --- a/meta/packages/linux/linux-rp-2.6.23+2.6.24-rc8/squashfs3.0-2.6.15.patch +++ /dev/null @@ -1,4189 +0,0 @@ - fs/Kconfig | 65 + - fs/Makefile | 1 - fs/squashfs/Makefile | 7 - fs/squashfs/inode.c | 2122 +++++++++++++++++++++++++++++++++++++++++ - fs/squashfs/squashfs.h | 86 + - fs/squashfs/squashfs2_0.c | 757 ++++++++++++++ - include/linux/squashfs_fs.h | 911 +++++++++++++++++ - include/linux/squashfs_fs_i.h | 45 - include/linux/squashfs_fs_sb.h | 74 + - init/do_mounts_rd.c | 13 - 10 files changed, 4081 insertions(+) - -Index: linux-2.6.22/fs/Kconfig -=================================================================== ---- linux-2.6.22.orig/fs/Kconfig 2007-08-28 21:56:32.000000000 +0100 -+++ linux-2.6.22/fs/Kconfig 2007-08-28 21:56:34.000000000 +0100 -@@ -1394,6 +1394,71 @@ config CRAMFS - - If unsure, say N. - -+config SQUASHFS -+ tristate "SquashFS 3.0 - Squashed file system support" -+ select ZLIB_INFLATE -+ help -+ Saying Y here includes support for SquashFS 3.0 (a Compressed Read-Only File -+ System). Squashfs is a highly compressed read-only filesystem for Linux. -+ It uses zlib compression to compress both files, inodes and directories. -+ Inodes in the system are very small and all blocks are packed to minimise -+ data overhead. Block sizes greater than 4K are supported up to a maximum of 64K. -+ SquashFS 3.0 supports 64 bit filesystems and files (larger than 4GB), full -+ uid/gid information, hard links and timestamps. -+ -+ Squashfs is intended for general read-only filesystem use, for archival -+ use (i.e. in cases where a .tar.gz file may be used), and in embedded -+ systems where low overhead is needed. Further information and filesystem tools -+ are available from http://squashfs.sourceforge.net. -+ -+ If you want to compile this as a module ( = code which can be -+ inserted in and removed from the running kernel whenever you want), -+ say M here and read <file:Documentation/modules.txt>. The module -+ will be called squashfs. Note that the root file system (the one -+ containing the directory /) cannot be compiled as a module. -+ -+ If unsure, say N. -+ -+config SQUASHFS_EMBEDDED -+ -+ bool "Additional options for memory-constrained systems" -+ depends on SQUASHFS -+ default n -+ help -+ Saying Y here allows you to specify cache sizes and how Squashfs -+ allocates memory. This is only intended for memory constrained -+ systems. -+ -+ If unsure, say N. -+ -+config SQUASHFS_FRAGMENT_CACHE_SIZE -+ int "Number of fragments cached" if SQUASHFS_EMBEDDED -+ depends on SQUASHFS -+ default "3" -+ help -+ By default SquashFS caches the last 3 fragments read from -+ the filesystem. Increasing this amount may mean SquashFS -+ has to re-read fragments less often from disk, at the expense -+ of extra system memory. Decreasing this amount will mean -+ SquashFS uses less memory at the expense of extra reads from disk. -+ -+ Note there must be at least one cached fragment. Anything -+ much more than three will probably not make much difference. -+ -+config SQUASHFS_VMALLOC -+ bool "Use Vmalloc rather than Kmalloc" if SQUASHFS_EMBEDDED -+ depends on SQUASHFS -+ default n -+ help -+ By default SquashFS uses kmalloc to obtain fragment cache memory. -+ Kmalloc memory is the standard kernel allocator, but it can fail -+ on memory constrained systems. Because of the way Vmalloc works, -+ Vmalloc can succeed when kmalloc fails. Specifying this option -+ will make SquashFS always use Vmalloc to allocate the -+ fragment cache memory. -+ -+ If unsure, say N. -+ - config VXFS_FS - tristate "FreeVxFS file system support (VERITAS VxFS(TM) compatible)" - depends on BLOCK -Index: linux-2.6.22/fs/Makefile -=================================================================== ---- linux-2.6.22.orig/fs/Makefile 2007-08-28 21:54:14.000000000 +0100 -+++ linux-2.6.22/fs/Makefile 2007-08-28 21:56:34.000000000 +0100 -@@ -72,6 +72,7 @@ obj-$(CONFIG_JBD) += jbd/ - obj-$(CONFIG_JBD2) += jbd2/ - obj-$(CONFIG_EXT2_FS) += ext2/ - obj-$(CONFIG_CRAMFS) += cramfs/ -+obj-$(CONFIG_SQUASHFS) += squashfs/ - obj-$(CONFIG_RAMFS) += ramfs/ - obj-$(CONFIG_HUGETLBFS) += hugetlbfs/ - obj-$(CONFIG_CODA_FS) += coda/ -Index: linux-2.6.22/fs/squashfs/inode.c -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/fs/squashfs/inode.c 2007-08-28 22:12:03.000000000 +0100 -@@ -0,0 +1,2122 @@ -+/* -+ * Squashfs - a compressed read only filesystem for Linux -+ * -+ * Copyright (c) 2002, 2003, 2004, 2005, 2006 -+ * Phillip Lougher <phillip@lougher.org.uk> -+ * -+ * 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 the Free Software Foundation; either version 2, -+ * or (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -+ * -+ * inode.c -+ */ -+ -+#include <linux/types.h> -+#include <linux/squashfs_fs.h> -+#include <linux/module.h> -+#include <linux/errno.h> -+#include <linux/slab.h> -+#include <linux/fs.h> -+#include <linux/smp_lock.h> -+#include <linux/slab.h> -+#include <linux/squashfs_fs_sb.h> -+#include <linux/squashfs_fs_i.h> -+#include <linux/buffer_head.h> -+#include <linux/vfs.h> -+#include <linux/init.h> -+#include <linux/dcache.h> -+#include <linux/wait.h> -+#include <linux/zlib.h> -+#include <linux/blkdev.h> -+#include <linux/vmalloc.h> -+#include <asm/uaccess.h> -+#include <asm/semaphore.h> -+ -+#include "squashfs.h" -+ -+static void squashfs_put_super(struct super_block *); -+static int squashfs_statfs(struct dentry *, struct kstatfs *); -+static int squashfs_symlink_readpage(struct file *file, struct page *page); -+static int squashfs_readpage(struct file *file, struct page *page); -+static int squashfs_readpage4K(struct file *file, struct page *page); -+static int squashfs_readdir(struct file *, void *, filldir_t); -+static struct inode *squashfs_alloc_inode(struct super_block *sb); -+static void squashfs_destroy_inode(struct inode *inode); -+static int init_inodecache(void); -+static void destroy_inodecache(void); -+static struct dentry *squashfs_lookup(struct inode *, struct dentry *, -+ struct nameidata *); -+static struct inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode); -+static long long read_blocklist(struct inode *inode, int index, -+ int readahead_blks, char *block_list, -+ unsigned short **block_p, unsigned int *bsize); -+static int squashfs_get_sb(struct file_system_type *, int, -+ const char *, void *, struct vfsmount *); -+ -+ -+static z_stream stream; -+ -+static struct file_system_type squashfs_fs_type = { -+ .owner = THIS_MODULE, -+ .name = "squashfs", -+ .get_sb = squashfs_get_sb, -+ .kill_sb = kill_block_super, -+ .fs_flags = FS_REQUIRES_DEV -+}; -+ -+static unsigned char squashfs_filetype_table[] = { -+ DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK -+}; -+ -+static struct super_operations squashfs_ops = { -+ .alloc_inode = squashfs_alloc_inode, -+ .destroy_inode = squashfs_destroy_inode, -+ .statfs = squashfs_statfs, -+ .put_super = squashfs_put_super, -+}; -+ -+SQSH_EXTERN struct address_space_operations squashfs_symlink_aops = { -+ .readpage = squashfs_symlink_readpage -+}; -+ -+SQSH_EXTERN struct address_space_operations squashfs_aops = { -+ .readpage = squashfs_readpage -+}; -+ -+SQSH_EXTERN struct address_space_operations squashfs_aops_4K = { -+ .readpage = squashfs_readpage4K -+}; -+ -+static struct file_operations squashfs_dir_ops = { -+ .read = generic_read_dir, -+ .readdir = squashfs_readdir -+}; -+ -+SQSH_EXTERN struct inode_operations squashfs_dir_inode_ops = { -+ .lookup = squashfs_lookup -+}; -+ -+ -+static struct buffer_head *get_block_length(struct super_block *s, -+ int *cur_index, int *offset, int *c_byte) -+{ -+ struct squashfs_sb_info *msblk = s->s_fs_info; -+ unsigned short temp; -+ struct buffer_head *bh; -+ -+ if (!(bh = sb_bread(s, *cur_index))) -+ goto out; -+ -+ if (msblk->devblksize - *offset == 1) { -+ if (msblk->swap) -+ ((unsigned char *) &temp)[1] = *((unsigned char *) -+ (bh->b_data + *offset)); -+ else -+ ((unsigned char *) &temp)[0] = *((unsigned char *) -+ (bh->b_data + *offset)); -+ brelse(bh); -+ if (!(bh = sb_bread(s, ++(*cur_index)))) -+ goto out; -+ if (msblk->swap) -+ ((unsigned char *) &temp)[0] = *((unsigned char *) -+ bh->b_data); -+ else -+ ((unsigned char *) &temp)[1] = *((unsigned char *) -+ bh->b_data); -+ *c_byte = temp; -+ *offset = 1; -+ } else { -+ if (msblk->swap) { -+ ((unsigned char *) &temp)[1] = *((unsigned char *) -+ (bh->b_data + *offset)); -+ ((unsigned char *) &temp)[0] = *((unsigned char *) -+ (bh->b_data + *offset + 1)); -+ } else { -+ ((unsigned char *) &temp)[0] = *((unsigned char *) -+ (bh->b_data + *offset)); -+ ((unsigned char *) &temp)[1] = *((unsigned char *) -+ (bh->b_data + *offset + 1)); -+ } -+ *c_byte = temp; -+ *offset += 2; -+ } -+ -+ if (SQUASHFS_CHECK_DATA(msblk->sblk.flags)) { -+ if (*offset == msblk->devblksize) { -+ brelse(bh); -+ if (!(bh = sb_bread(s, ++(*cur_index)))) -+ goto out; -+ *offset = 0; -+ } -+ if (*((unsigned char *) (bh->b_data + *offset)) != -+ SQUASHFS_MARKER_BYTE) { -+ ERROR("Metadata block marker corrupt @ %x\n", -+ *cur_index); -+ brelse(bh); -+ goto out; -+ } -+ (*offset)++; -+ } -+ return bh; -+ -+out: -+ return NULL; -+} -+ -+ -+SQSH_EXTERN unsigned int squashfs_read_data(struct super_block *s, char *buffer, -+ long long index, unsigned int length, -+ long long *next_index) -+{ -+ struct squashfs_sb_info *msblk = s->s_fs_info; -+ struct buffer_head *bh[((SQUASHFS_FILE_MAX_SIZE - 1) >> -+ msblk->devblksize_log2) + 2]; -+ unsigned int offset = index & ((1 << msblk->devblksize_log2) - 1); -+ unsigned int cur_index = index >> msblk->devblksize_log2; -+ int bytes, avail_bytes, b = 0, k; -+ char *c_buffer; -+ unsigned int compressed; -+ unsigned int c_byte = length; -+ -+ if (c_byte) { -+ bytes = msblk->devblksize - offset; -+ compressed = SQUASHFS_COMPRESSED_BLOCK(c_byte); -+ c_buffer = compressed ? msblk->read_data : buffer; -+ c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte); -+ -+ TRACE("Block @ 0x%llx, %scompressed size %d\n", index, compressed -+ ? "" : "un", (unsigned int) c_byte); -+ -+ if (!(bh[0] = sb_getblk(s, cur_index))) -+ goto block_release; -+ -+ for (b = 1; bytes < c_byte; b++) { -+ if (!(bh[b] = sb_getblk(s, ++cur_index))) -+ goto block_release; -+ bytes += msblk->devblksize; -+ } -+ ll_rw_block(READ, b, bh); -+ } else { -+ if (!(bh[0] = get_block_length(s, &cur_index, &offset, -+ &c_byte))) -+ goto read_failure; -+ -+ bytes = msblk->devblksize - offset; -+ compressed = SQUASHFS_COMPRESSED(c_byte); -+ c_buffer = compressed ? msblk->read_data : buffer; -+ c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte); -+ -+ TRACE("Block @ 0x%llx, %scompressed size %d\n", index, compressed -+ ? "" : "un", (unsigned int) c_byte); -+ -+ for (b = 1; bytes < c_byte; b++) { -+ if (!(bh[b] = sb_getblk(s, ++cur_index))) -+ goto block_release; -+ bytes += msblk->devblksize; -+ } -+ ll_rw_block(READ, b - 1, bh + 1); -+ } -+ -+ if (compressed) -+ down(&msblk->read_data_mutex); -+ -+ for (bytes = 0, k = 0; k < b; k++) { -+ avail_bytes = (c_byte - bytes) > (msblk->devblksize - offset) ? -+ msblk->devblksize - offset : -+ c_byte - bytes; -+ wait_on_buffer(bh[k]); -+ if (!buffer_uptodate(bh[k])) -+ goto block_release; -+ memcpy(c_buffer + bytes, bh[k]->b_data + offset, avail_bytes); -+ bytes += avail_bytes; -+ offset = 0; -+ brelse(bh[k]); -+ } -+ -+ /* -+ * uncompress block -+ */ -+ if (compressed) { -+ int zlib_err; -+ -+ stream.next_in = c_buffer; -+ stream.avail_in = c_byte; -+ stream.next_out = buffer; -+ stream.avail_out = msblk->read_size; -+ -+ if (((zlib_err = zlib_inflateInit(&stream)) != Z_OK) || -+ ((zlib_err = zlib_inflate(&stream, Z_FINISH)) -+ != Z_STREAM_END) || ((zlib_err = -+ zlib_inflateEnd(&stream)) != Z_OK)) { -+ ERROR("zlib_fs returned unexpected result 0x%x\n", -+ zlib_err); -+ bytes = 0; -+ } else -+ bytes = stream.total_out; -+ -+ up(&msblk->read_data_mutex); -+ } -+ -+ if (next_index) -+ *next_index = index + c_byte + (length ? 0 : -+ (SQUASHFS_CHECK_DATA(msblk->sblk.flags) -+ ? 3 : 2)); -+ return bytes; -+ -+block_release: -+ while (--b >= 0) -+ brelse(bh[b]); -+ -+read_failure: -+ ERROR("sb_bread failed reading block 0x%x\n", cur_index); -+ return 0; -+} -+ -+ -+SQSH_EXTERN int squashfs_get_cached_block(struct super_block *s, char *buffer, -+ long long block, unsigned int offset, -+ int length, long long *next_block, -+ unsigned int *next_offset) -+{ -+ struct squashfs_sb_info *msblk = s->s_fs_info; -+ int n, i, bytes, return_length = length; -+ long long next_index; -+ -+ TRACE("Entered squashfs_get_cached_block [%llx:%x]\n", block, offset); -+ -+ while ( 1 ) { -+ for (i = 0; i < SQUASHFS_CACHED_BLKS; i++) -+ if (msblk->block_cache[i].block == block) -+ break; -+ -+ down(&msblk->block_cache_mutex); -+ -+ if (i == SQUASHFS_CACHED_BLKS) { -+ /* read inode header block */ -+ for (i = msblk->next_cache, n = SQUASHFS_CACHED_BLKS; -+ n ; n --, i = (i + 1) % -+ SQUASHFS_CACHED_BLKS) -+ if (msblk->block_cache[i].block != -+ SQUASHFS_USED_BLK) -+ break; -+ -+ if (n == 0) { -+ wait_queue_t wait; -+ -+ init_waitqueue_entry(&wait, current); -+ add_wait_queue(&msblk->waitq, &wait); -+ set_current_state(TASK_UNINTERRUPTIBLE); -+ up(&msblk->block_cache_mutex); -+ schedule(); -+ set_current_state(TASK_RUNNING); -+ remove_wait_queue(&msblk->waitq, &wait); -+ continue; -+ } -+ msblk->next_cache = (i + 1) % SQUASHFS_CACHED_BLKS; -+ -+ if (msblk->block_cache[i].block == -+ SQUASHFS_INVALID_BLK) { -+ if (!(msblk->block_cache[i].data = -+ kmalloc(SQUASHFS_METADATA_SIZE, -+ GFP_KERNEL))) { -+ ERROR("Failed to allocate cache" -+ "block\n"); -+ up(&msblk->block_cache_mutex); -+ goto out; -+ } -+ } -+ -+ msblk->block_cache[i].block = SQUASHFS_USED_BLK; -+ up(&msblk->block_cache_mutex); -+ -+ if (!(msblk->block_cache[i].length = -+ squashfs_read_data(s, -+ msblk->block_cache[i].data, -+ block, 0, &next_index))) { -+ ERROR("Unable to read cache block [%llx:%x]\n", -+ block, offset); -+ goto out; -+ } -+ -+ down(&msblk->block_cache_mutex); -+ wake_up(&msblk->waitq); -+ msblk->block_cache[i].block = block; -+ msblk->block_cache[i].next_index = next_index; -+ TRACE("Read cache block [%llx:%x]\n", block, offset); -+ } -+ -+ if (msblk->block_cache[i].block != block) { -+ up(&msblk->block_cache_mutex); -+ continue; -+ } -+ -+ if ((bytes = msblk->block_cache[i].length - offset) >= length) { -+ if (buffer) -+ memcpy(buffer, msblk->block_cache[i].data + -+ offset, length); -+ if (msblk->block_cache[i].length - offset == length) { -+ *next_block = msblk->block_cache[i].next_index; -+ *next_offset = 0; -+ } else { -+ *next_block = block; -+ *next_offset = offset + length; -+ } -+ up(&msblk->block_cache_mutex); -+ goto finish; -+ } else { -+ if (buffer) { -+ memcpy(buffer, msblk->block_cache[i].data + -+ offset, bytes); -+ buffer += bytes; -+ } -+ block = msblk->block_cache[i].next_index; -+ up(&msblk->block_cache_mutex); -+ length -= bytes; -+ offset = 0; -+ } -+ } -+ -+finish: -+ return return_length; -+out: -+ return 0; -+} -+ -+ -+static int get_fragment_location(struct super_block *s, unsigned int fragment, -+ long long *fragment_start_block, -+ unsigned int *fragment_size) -+{ -+ struct squashfs_sb_info *msblk = s->s_fs_info; -+ long long start_block = -+ msblk->fragment_index[SQUASHFS_FRAGMENT_INDEX(fragment)]; -+ int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET(fragment); -+ struct squashfs_fragment_entry fragment_entry; -+ -+ if (msblk->swap) { -+ struct squashfs_fragment_entry sfragment_entry; -+ -+ if (!squashfs_get_cached_block(s, (char *) &sfragment_entry, -+ start_block, offset, -+ sizeof(sfragment_entry), &start_block, -+ &offset)) -+ goto out; -+ SQUASHFS_SWAP_FRAGMENT_ENTRY(&fragment_entry, &sfragment_entry); -+ } else -+ if (!squashfs_get_cached_block(s, (char *) &fragment_entry, -+ start_block, offset, -+ sizeof(fragment_entry), &start_block, -+ &offset)) -+ goto out; -+ -+ *fragment_start_block = fragment_entry.start_block; -+ *fragment_size = fragment_entry.size; -+ -+ return 1; -+ -+out: -+ return 0; -+} -+ -+ -+SQSH_EXTERN void release_cached_fragment(struct squashfs_sb_info *msblk, struct -+ squashfs_fragment_cache *fragment) -+{ -+ down(&msblk->fragment_mutex); -+ fragment->locked --; -+ wake_up(&msblk->fragment_wait_queue); -+ up(&msblk->fragment_mutex); -+} -+ -+ -+SQSH_EXTERN struct squashfs_fragment_cache *get_cached_fragment(struct super_block -+ *s, long long start_block, -+ int length) -+{ -+ int i, n, nf; -+ struct squashfs_sb_info *msblk = s->s_fs_info; -+ -+ while ( 1 ) { -+ down(&msblk->fragment_mutex); -+ -+ for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS && -+ msblk->fragment[i].block != start_block; i++); -+ -+ if (i == SQUASHFS_CACHED_FRAGMENTS) { -+ nf = (msblk->next_fragment + 1) % -+ SQUASHFS_CACHED_FRAGMENTS; -+ for (i = msblk->next_fragment, n = -+ SQUASHFS_CACHED_FRAGMENTS; n && -+ msblk->fragment[i].locked; n--, i = (i + 1) % -+ SQUASHFS_CACHED_FRAGMENTS); -+ -+ if (n == 0) { -+ wait_queue_t wait; -+ -+ init_waitqueue_entry(&wait, current); -+ add_wait_queue(&msblk->fragment_wait_queue, -+ &wait); -+ set_current_state(TASK_UNINTERRUPTIBLE); -+ up(&msblk->fragment_mutex); -+ schedule(); -+ set_current_state(TASK_RUNNING); -+ remove_wait_queue(&msblk->fragment_wait_queue, -+ &wait); -+ continue; -+ } -+ msblk->next_fragment = nf; -+ -+ if (msblk->fragment[i].data == NULL) -+ if (!(msblk->fragment[i].data = SQUASHFS_ALLOC -+ (SQUASHFS_FILE_MAX_SIZE))) { -+ ERROR("Failed to allocate fragment " -+ "cache block\n"); -+ up(&msblk->fragment_mutex); -+ goto out; -+ } -+ -+ msblk->fragment[i].block = SQUASHFS_INVALID_BLK; -+ msblk->fragment[i].locked = 1; -+ up(&msblk->fragment_mutex); -+ -+ if (!(msblk->fragment[i].length = squashfs_read_data(s, -+ msblk->fragment[i].data, -+ start_block, length, NULL))) { -+ ERROR("Unable to read fragment cache block " -+ "[%llx]\n", start_block); -+ msblk->fragment[i].locked = 0; -+ goto out; -+ } -+ -+ msblk->fragment[i].block = start_block; -+ TRACE("New fragment %d, start block %lld, locked %d\n", -+ i, msblk->fragment[i].block, -+ msblk->fragment[i].locked); -+ break; -+ } -+ -+ msblk->fragment[i].locked++; -+ up(&msblk->fragment_mutex); -+ TRACE("Got fragment %d, start block %lld, locked %d\n", i, -+ msblk->fragment[i].block, -+ msblk->fragment[i].locked); -+ break; -+ } -+ -+ return &msblk->fragment[i]; -+ -+out: -+ return NULL; -+} -+ -+ -+static struct inode *squashfs_new_inode(struct super_block *s, -+ struct squashfs_base_inode_header *inodeb) -+{ -+ struct squashfs_sb_info *msblk = s->s_fs_info; -+ struct inode *i = new_inode(s); -+ -+ if (i) { -+ i->i_ino = inodeb->inode_number; -+ i->i_mtime.tv_sec = inodeb->mtime; -+ i->i_atime.tv_sec = inodeb->mtime; -+ i->i_ctime.tv_sec = inodeb->mtime; -+ i->i_uid = msblk->uid[inodeb->uid]; -+ i->i_mode = inodeb->mode; -+ i->i_size = 0; -+ if (inodeb->guid == SQUASHFS_GUIDS) -+ i->i_gid = i->i_uid; -+ else -+ i->i_gid = msblk->guid[inodeb->guid]; -+ } -+ -+ return i; -+} -+ -+ -+static struct inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode) -+{ -+ struct inode *i; -+ struct squashfs_sb_info *msblk = s->s_fs_info; -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ long long block = SQUASHFS_INODE_BLK(inode) + -+ sblk->inode_table_start; -+ unsigned int offset = SQUASHFS_INODE_OFFSET(inode); -+ long long next_block; -+ unsigned int next_offset; -+ union squashfs_inode_header id, sid; -+ struct squashfs_base_inode_header *inodeb = &id.base, -+ *sinodeb = &sid.base; -+ -+ TRACE("Entered squashfs_iget\n"); -+ -+ if (msblk->swap) { -+ if (!squashfs_get_cached_block(s, (char *) sinodeb, block, -+ offset, sizeof(*sinodeb), &next_block, -+ &next_offset)) -+ goto failed_read; -+ SQUASHFS_SWAP_BASE_INODE_HEADER(inodeb, sinodeb, -+ sizeof(*sinodeb)); -+ } else -+ if (!squashfs_get_cached_block(s, (char *) inodeb, block, -+ offset, sizeof(*inodeb), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ switch(inodeb->inode_type) { -+ case SQUASHFS_FILE_TYPE: { -+ unsigned int frag_size; -+ long long frag_blk; -+ struct squashfs_reg_inode_header *inodep = &id.reg; -+ struct squashfs_reg_inode_header *sinodep = &sid.reg; -+ -+ if (msblk->swap) { -+ if (!squashfs_get_cached_block(s, (char *) -+ sinodep, block, offset, -+ sizeof(*sinodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ SQUASHFS_SWAP_REG_INODE_HEADER(inodep, sinodep); -+ } else -+ if (!squashfs_get_cached_block(s, (char *) -+ inodep, block, offset, -+ sizeof(*inodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ frag_blk = SQUASHFS_INVALID_BLK; -+ if (inodep->fragment != SQUASHFS_INVALID_FRAG && -+ !get_fragment_location(s, -+ inodep->fragment, &frag_blk, &frag_size)) -+ goto failed_read; -+ -+ if((i = squashfs_new_inode(s, inodeb)) == NULL) -+ goto failed_read1; -+ -+ i->i_nlink = 1; -+ i->i_size = inodep->file_size; -+ i->i_fop = &generic_ro_fops; -+ i->i_mode |= S_IFREG; -+ i->i_blocks = ((i->i_size - 1) >> 9) + 1; -+ SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk; -+ SQUASHFS_I(i)->u.s1.fragment_size = frag_size; -+ SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset; -+ SQUASHFS_I(i)->start_block = inodep->start_block; -+ SQUASHFS_I(i)->u.s1.block_list_start = next_block; -+ SQUASHFS_I(i)->offset = next_offset; -+ if (sblk->block_size > 4096) -+ i->i_data.a_ops = &squashfs_aops; -+ else -+ i->i_data.a_ops = &squashfs_aops_4K; -+ -+ TRACE("File inode %x:%x, start_block %llx, " -+ "block_list_start %llx, offset %x\n", -+ SQUASHFS_INODE_BLK(inode), offset, -+ inodep->start_block, next_block, -+ next_offset); -+ break; -+ } -+ case SQUASHFS_LREG_TYPE: { -+ unsigned int frag_size; -+ long long frag_blk; -+ struct squashfs_lreg_inode_header *inodep = &id.lreg; -+ struct squashfs_lreg_inode_header *sinodep = &sid.lreg; -+ -+ if (msblk->swap) { -+ if (!squashfs_get_cached_block(s, (char *) -+ sinodep, block, offset, -+ sizeof(*sinodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ SQUASHFS_SWAP_LREG_INODE_HEADER(inodep, sinodep); -+ } else -+ if (!squashfs_get_cached_block(s, (char *) -+ inodep, block, offset, -+ sizeof(*inodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ frag_blk = SQUASHFS_INVALID_BLK; -+ if (inodep->fragment != SQUASHFS_INVALID_FRAG && -+ !get_fragment_location(s, -+ inodep->fragment, &frag_blk, &frag_size)) -+ goto failed_read; -+ -+ if((i = squashfs_new_inode(s, inodeb)) == NULL) -+ goto failed_read1; -+ -+ i->i_nlink = inodep->nlink; -+ i->i_size = inodep->file_size; -+ i->i_fop = &generic_ro_fops; -+ i->i_mode |= S_IFREG; -+ i->i_blocks = ((i->i_size - 1) >> 9) + 1; -+ SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk; -+ SQUASHFS_I(i)->u.s1.fragment_size = frag_size; -+ SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset; -+ SQUASHFS_I(i)->start_block = inodep->start_block; -+ SQUASHFS_I(i)->u.s1.block_list_start = next_block; -+ SQUASHFS_I(i)->offset = next_offset; -+ if (sblk->block_size > 4096) -+ i->i_data.a_ops = &squashfs_aops; -+ else -+ i->i_data.a_ops = &squashfs_aops_4K; -+ -+ TRACE("File inode %x:%x, start_block %llx, " -+ "block_list_start %llx, offset %x\n", -+ SQUASHFS_INODE_BLK(inode), offset, -+ inodep->start_block, next_block, -+ next_offset); -+ break; -+ } -+ case SQUASHFS_DIR_TYPE: { -+ struct squashfs_dir_inode_header *inodep = &id.dir; -+ struct squashfs_dir_inode_header *sinodep = &sid.dir; -+ -+ if (msblk->swap) { -+ if (!squashfs_get_cached_block(s, (char *) -+ sinodep, block, offset, -+ sizeof(*sinodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ SQUASHFS_SWAP_DIR_INODE_HEADER(inodep, sinodep); -+ } else -+ if (!squashfs_get_cached_block(s, (char *) -+ inodep, block, offset, -+ sizeof(*inodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ if((i = squashfs_new_inode(s, inodeb)) == NULL) -+ goto failed_read1; -+ -+ i->i_nlink = inodep->nlink; -+ i->i_size = inodep->file_size; -+ i->i_op = &squashfs_dir_inode_ops; -+ i->i_fop = &squashfs_dir_ops; -+ i->i_mode |= S_IFDIR; -+ SQUASHFS_I(i)->start_block = inodep->start_block; -+ SQUASHFS_I(i)->offset = inodep->offset; -+ SQUASHFS_I(i)->u.s2.directory_index_count = 0; -+ SQUASHFS_I(i)->u.s2.parent_inode = inodep->parent_inode; -+ -+ TRACE("Directory inode %x:%x, start_block %x, offset " -+ "%x\n", SQUASHFS_INODE_BLK(inode), -+ offset, inodep->start_block, -+ inodep->offset); -+ break; -+ } -+ case SQUASHFS_LDIR_TYPE: { -+ struct squashfs_ldir_inode_header *inodep = &id.ldir; -+ struct squashfs_ldir_inode_header *sinodep = &sid.ldir; -+ -+ if (msblk->swap) { -+ if (!squashfs_get_cached_block(s, (char *) -+ sinodep, block, offset, -+ sizeof(*sinodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ SQUASHFS_SWAP_LDIR_INODE_HEADER(inodep, -+ sinodep); -+ } else -+ if (!squashfs_get_cached_block(s, (char *) -+ inodep, block, offset, -+ sizeof(*inodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ if((i = squashfs_new_inode(s, inodeb)) == NULL) -+ goto failed_read1; -+ -+ i->i_nlink = inodep->nlink; -+ i->i_size = inodep->file_size; -+ i->i_op = &squashfs_dir_inode_ops; -+ i->i_fop = &squashfs_dir_ops; -+ i->i_mode |= S_IFDIR; -+ SQUASHFS_I(i)->start_block = inodep->start_block; -+ SQUASHFS_I(i)->offset = inodep->offset; -+ SQUASHFS_I(i)->u.s2.directory_index_start = next_block; -+ SQUASHFS_I(i)->u.s2.directory_index_offset = -+ next_offset; -+ SQUASHFS_I(i)->u.s2.directory_index_count = -+ inodep->i_count; -+ SQUASHFS_I(i)->u.s2.parent_inode = inodep->parent_inode; -+ -+ TRACE("Long directory inode %x:%x, start_block %x, " -+ "offset %x\n", -+ SQUASHFS_INODE_BLK(inode), offset, -+ inodep->start_block, inodep->offset); -+ break; -+ } -+ case SQUASHFS_SYMLINK_TYPE: { -+ struct squashfs_symlink_inode_header *inodep = -+ &id.symlink; -+ struct squashfs_symlink_inode_header *sinodep = -+ &sid.symlink; -+ -+ if (msblk->swap) { -+ if (!squashfs_get_cached_block(s, (char *) -+ sinodep, block, offset, -+ sizeof(*sinodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ SQUASHFS_SWAP_SYMLINK_INODE_HEADER(inodep, -+ sinodep); -+ } else -+ if (!squashfs_get_cached_block(s, (char *) -+ inodep, block, offset, -+ sizeof(*inodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ if((i = squashfs_new_inode(s, inodeb)) == NULL) -+ goto failed_read1; -+ -+ i->i_nlink = inodep->nlink; -+ i->i_size = inodep->symlink_size; -+ i->i_op = &page_symlink_inode_operations; -+ i->i_data.a_ops = &squashfs_symlink_aops; -+ i->i_mode |= S_IFLNK; -+ SQUASHFS_I(i)->start_block = next_block; -+ SQUASHFS_I(i)->offset = next_offset; -+ -+ TRACE("Symbolic link inode %x:%x, start_block %llx, " -+ "offset %x\n", -+ SQUASHFS_INODE_BLK(inode), offset, -+ next_block, next_offset); -+ break; -+ } -+ case SQUASHFS_BLKDEV_TYPE: -+ case SQUASHFS_CHRDEV_TYPE: { -+ struct squashfs_dev_inode_header *inodep = &id.dev; -+ struct squashfs_dev_inode_header *sinodep = &sid.dev; -+ -+ if (msblk->swap) { -+ if (!squashfs_get_cached_block(s, (char *) -+ sinodep, block, offset, -+ sizeof(*sinodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ SQUASHFS_SWAP_DEV_INODE_HEADER(inodep, sinodep); -+ } else -+ if (!squashfs_get_cached_block(s, (char *) -+ inodep, block, offset, -+ sizeof(*inodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ if ((i = squashfs_new_inode(s, inodeb)) == NULL) -+ goto failed_read1; -+ -+ i->i_nlink = inodep->nlink; -+ i->i_mode |= (inodeb->inode_type == -+ SQUASHFS_CHRDEV_TYPE) ? S_IFCHR : -+ S_IFBLK; -+ init_special_inode(i, i->i_mode, -+ old_decode_dev(inodep->rdev)); -+ -+ TRACE("Device inode %x:%x, rdev %x\n", -+ SQUASHFS_INODE_BLK(inode), offset, -+ inodep->rdev); -+ break; -+ } -+ case SQUASHFS_FIFO_TYPE: -+ case SQUASHFS_SOCKET_TYPE: { -+ struct squashfs_ipc_inode_header *inodep = &id.ipc; -+ struct squashfs_ipc_inode_header *sinodep = &sid.ipc; -+ -+ if (msblk->swap) { -+ if (!squashfs_get_cached_block(s, (char *) -+ sinodep, block, offset, -+ sizeof(*sinodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ SQUASHFS_SWAP_IPC_INODE_HEADER(inodep, sinodep); -+ } else -+ if (!squashfs_get_cached_block(s, (char *) -+ inodep, block, offset, -+ sizeof(*inodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ if ((i = squashfs_new_inode(s, inodeb)) == NULL) -+ goto failed_read1; -+ -+ i->i_nlink = inodep->nlink; -+ i->i_mode |= (inodeb->inode_type == SQUASHFS_FIFO_TYPE) -+ ? S_IFIFO : S_IFSOCK; -+ init_special_inode(i, i->i_mode, 0); -+ break; -+ } -+ default: -+ ERROR("Unknown inode type %d in squashfs_iget!\n", -+ inodeb->inode_type); -+ goto failed_read1; -+ } -+ -+ insert_inode_hash(i); -+ return i; -+ -+failed_read: -+ ERROR("Unable to read inode [%llx:%x]\n", block, offset); -+ -+failed_read1: -+ return NULL; -+} -+ -+ -+static int read_fragment_index_table(struct super_block *s) -+{ -+ struct squashfs_sb_info *msblk = s->s_fs_info; -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ -+ /* Allocate fragment index table */ -+ if (!(msblk->fragment_index = kmalloc(SQUASHFS_FRAGMENT_INDEX_BYTES -+ (sblk->fragments), GFP_KERNEL))) { -+ ERROR("Failed to allocate uid/gid table\n"); -+ return 0; -+ } -+ -+ if (SQUASHFS_FRAGMENT_INDEX_BYTES(sblk->fragments) && -+ !squashfs_read_data(s, (char *) -+ msblk->fragment_index, -+ sblk->fragment_table_start, -+ SQUASHFS_FRAGMENT_INDEX_BYTES -+ (sblk->fragments) | -+ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { -+ ERROR("unable to read fragment index table\n"); -+ return 0; -+ } -+ -+ if (msblk->swap) { -+ int i; -+ long long fragment; -+ -+ for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES(sblk->fragments); -+ i++) { -+ SQUASHFS_SWAP_FRAGMENT_INDEXES((&fragment), -+ &msblk->fragment_index[i], 1); -+ msblk->fragment_index[i] = fragment; -+ } -+ } -+ -+ return 1; -+} -+ -+ -+static int supported_squashfs_filesystem(struct squashfs_sb_info *msblk, int silent) -+{ -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ -+ msblk->iget = squashfs_iget; -+ msblk->read_blocklist = read_blocklist; -+ msblk->read_fragment_index_table = read_fragment_index_table; -+ -+ if (sblk->s_major == 1) { -+ if (!squashfs_1_0_supported(msblk)) { -+ SERROR("Major/Minor mismatch, Squashfs 1.0 filesystems " -+ "are unsupported\n"); -+ SERROR("Please recompile with " -+ "Squashfs 1.0 support enabled\n"); -+ return 0; -+ } -+ } else if (sblk->s_major == 2) { -+ if (!squashfs_2_0_supported(msblk)) { -+ SERROR("Major/Minor mismatch, Squashfs 2.0 filesystems " -+ "are unsupported\n"); -+ SERROR("Please recompile with " -+ "Squashfs 2.0 support enabled\n"); -+ return 0; -+ } -+ } else if(sblk->s_major != SQUASHFS_MAJOR || sblk->s_minor > -+ SQUASHFS_MINOR) { -+ SERROR("Major/Minor mismatch, trying to mount newer %d.%d " -+ "filesystem\n", sblk->s_major, sblk->s_minor); -+ SERROR("Please update your kernel\n"); -+ return 0; -+ } -+ -+ return 1; -+} -+ -+ -+static int squashfs_fill_super(struct super_block *s, void *data, int silent) -+{ -+ struct squashfs_sb_info *msblk; -+ struct squashfs_super_block *sblk; -+ int i; -+ char b[BDEVNAME_SIZE]; -+ struct inode *root; -+ -+ TRACE("Entered squashfs_read_superblock\n"); -+ -+ if (!(s->s_fs_info = kmalloc(sizeof(struct squashfs_sb_info), -+ GFP_KERNEL))) { -+ ERROR("Failed to allocate superblock\n"); -+ goto failure; -+ } -+ memset(s->s_fs_info, 0, sizeof(struct squashfs_sb_info)); -+ msblk = s->s_fs_info; -+ sblk = &msblk->sblk; -+ -+ msblk->devblksize = sb_min_blocksize(s, BLOCK_SIZE); -+ msblk->devblksize_log2 = ffz(~msblk->devblksize); -+ -+ init_MUTEX(&msblk->read_data_mutex); -+ init_MUTEX(&msblk->read_page_mutex); -+ init_MUTEX(&msblk->block_cache_mutex); -+ init_MUTEX(&msblk->fragment_mutex); -+ init_MUTEX(&msblk->meta_index_mutex); -+ -+ init_waitqueue_head(&msblk->waitq); -+ init_waitqueue_head(&msblk->fragment_wait_queue); -+ -+ if (!squashfs_read_data(s, (char *) sblk, SQUASHFS_START, -+ sizeof(struct squashfs_super_block) | -+ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { -+ SERROR("unable to read superblock\n"); -+ goto failed_mount; -+ } -+ -+ /* Check it is a SQUASHFS superblock */ -+ msblk->swap = 0; -+ if ((s->s_magic = sblk->s_magic) != SQUASHFS_MAGIC) { -+ if (sblk->s_magic == SQUASHFS_MAGIC_SWAP) { -+ struct squashfs_super_block ssblk; -+ -+ WARNING("Mounting a different endian SQUASHFS " -+ "filesystem on %s\n", bdevname(s->s_bdev, b)); -+ -+ SQUASHFS_SWAP_SUPER_BLOCK(&ssblk, sblk); -+ memcpy(sblk, &ssblk, sizeof(struct squashfs_super_block)); -+ msblk->swap = 1; -+ } else { -+ SERROR("Can't find a SQUASHFS superblock on %s\n", -+ bdevname(s->s_bdev, b)); -+ goto failed_mount; -+ } -+ } -+ -+ /* Check the MAJOR & MINOR versions */ -+ if(!supported_squashfs_filesystem(msblk, silent)) -+ goto failed_mount; -+ -+ TRACE("Found valid superblock on %s\n", bdevname(s->s_bdev, b)); -+ TRACE("Inodes are %scompressed\n", -+ SQUASHFS_UNCOMPRESSED_INODES -+ (sblk->flags) ? "un" : ""); -+ TRACE("Data is %scompressed\n", -+ SQUASHFS_UNCOMPRESSED_DATA(sblk->flags) -+ ? "un" : ""); -+ TRACE("Check data is %s present in the filesystem\n", -+ SQUASHFS_CHECK_DATA(sblk->flags) ? -+ "" : "not"); -+ TRACE("Filesystem size %lld bytes\n", sblk->bytes_used); -+ TRACE("Block size %d\n", sblk->block_size); -+ TRACE("Number of inodes %d\n", sblk->inodes); -+ if (sblk->s_major > 1) -+ TRACE("Number of fragments %d\n", sblk->fragments); -+ TRACE("Number of uids %d\n", sblk->no_uids); -+ TRACE("Number of gids %d\n", sblk->no_guids); -+ TRACE("sblk->inode_table_start %llx\n", sblk->inode_table_start); -+ TRACE("sblk->directory_table_start %llx\n", sblk->directory_table_start); -+ if (sblk->s_major > 1) -+ TRACE("sblk->fragment_table_start %llx\n", -+ sblk->fragment_table_start); -+ TRACE("sblk->uid_start %llx\n", sblk->uid_start); -+ -+ s->s_flags |= MS_RDONLY; -+ s->s_op = &squashfs_ops; -+ -+ /* Init inode_table block pointer array */ -+ if (!(msblk->block_cache = kmalloc(sizeof(struct squashfs_cache) * -+ SQUASHFS_CACHED_BLKS, GFP_KERNEL))) { -+ ERROR("Failed to allocate block cache\n"); -+ goto failed_mount; -+ } -+ -+ for (i = 0; i < SQUASHFS_CACHED_BLKS; i++) -+ msblk->block_cache[i].block = SQUASHFS_INVALID_BLK; -+ -+ msblk->next_cache = 0; -+ -+ /* Allocate read_data block */ -+ msblk->read_size = (sblk->block_size < SQUASHFS_METADATA_SIZE) ? -+ SQUASHFS_METADATA_SIZE : -+ sblk->block_size; -+ -+ if (!(msblk->read_data = kmalloc(msblk->read_size, GFP_KERNEL))) { -+ ERROR("Failed to allocate read_data block\n"); -+ goto failed_mount; -+ } -+ -+ /* Allocate read_page block */ -+ if (!(msblk->read_page = kmalloc(sblk->block_size, GFP_KERNEL))) { -+ ERROR("Failed to allocate read_page block\n"); -+ goto failed_mount; -+ } -+ -+ /* Allocate uid and gid tables */ -+ if (!(msblk->uid = kmalloc((sblk->no_uids + sblk->no_guids) * -+ sizeof(unsigned int), GFP_KERNEL))) { -+ ERROR("Failed to allocate uid/gid table\n"); -+ goto failed_mount; -+ } -+ msblk->guid = msblk->uid + sblk->no_uids; -+ -+ if (msblk->swap) { -+ unsigned int suid[sblk->no_uids + sblk->no_guids]; -+ -+ if (!squashfs_read_data(s, (char *) &suid, sblk->uid_start, -+ ((sblk->no_uids + sblk->no_guids) * -+ sizeof(unsigned int)) | -+ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { -+ ERROR("unable to read uid/gid table\n"); -+ goto failed_mount; -+ } -+ -+ SQUASHFS_SWAP_DATA(msblk->uid, suid, (sblk->no_uids + -+ sblk->no_guids), (sizeof(unsigned int) * 8)); -+ } else -+ if (!squashfs_read_data(s, (char *) msblk->uid, sblk->uid_start, -+ ((sblk->no_uids + sblk->no_guids) * -+ sizeof(unsigned int)) | -+ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { -+ ERROR("unable to read uid/gid table\n"); -+ goto failed_mount; -+ } -+ -+ -+ if (sblk->s_major == 1 && squashfs_1_0_supported(msblk)) -+ goto allocate_root; -+ -+ if (!(msblk->fragment = kmalloc(sizeof(struct squashfs_fragment_cache) * -+ SQUASHFS_CACHED_FRAGMENTS, GFP_KERNEL))) { -+ ERROR("Failed to allocate fragment block cache\n"); -+ goto failed_mount; -+ } -+ -+ for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++) { -+ msblk->fragment[i].locked = 0; -+ msblk->fragment[i].block = SQUASHFS_INVALID_BLK; -+ msblk->fragment[i].data = NULL; -+ } -+ -+ msblk->next_fragment = 0; -+ -+ /* Allocate fragment index table */ -+ if (msblk->read_fragment_index_table(s) == 0) -+ goto failed_mount; -+ -+allocate_root: -+ if ((root = (msblk->iget)(s, sblk->root_inode)) == NULL) -+ goto failed_mount; -+ -+ if ((s->s_root = d_alloc_root(root)) == NULL) { -+ ERROR("Root inode create failed\n"); -+ iput(root); -+ goto failed_mount; -+ } -+ -+ TRACE("Leaving squashfs_read_super\n"); -+ return 0; -+ -+failed_mount: -+ kfree(msblk->fragment_index); -+ kfree(msblk->fragment); -+ kfree(msblk->uid); -+ kfree(msblk->read_page); -+ kfree(msblk->read_data); -+ kfree(msblk->block_cache); -+ kfree(msblk->fragment_index_2); -+ kfree(s->s_fs_info); -+ s->s_fs_info = NULL; -+ return -EINVAL; -+ -+failure: -+ return -ENOMEM; -+} -+ -+ -+static int squashfs_statfs(struct dentry *dentry, struct kstatfs *buf) -+{ -+ struct super_block *s = dentry->d_sb; -+ struct squashfs_sb_info *msblk = s->s_fs_info; -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ -+ TRACE("Entered squashfs_statfs\n"); -+ -+ buf->f_type = SQUASHFS_MAGIC; -+ buf->f_bsize = sblk->block_size; -+ buf->f_blocks = ((sblk->bytes_used - 1) >> sblk->block_log) + 1; -+ buf->f_bfree = buf->f_bavail = 0; -+ buf->f_files = sblk->inodes; -+ buf->f_ffree = 0; -+ buf->f_namelen = SQUASHFS_NAME_LEN; -+ -+ return 0; -+} -+ -+ -+static int squashfs_symlink_readpage(struct file *file, struct page *page) -+{ -+ struct inode *inode = page->mapping->host; -+ int index = page->index << PAGE_CACHE_SHIFT, length, bytes; -+ long long block = SQUASHFS_I(inode)->start_block; -+ int offset = SQUASHFS_I(inode)->offset; -+ void *pageaddr = kmap(page); -+ -+ TRACE("Entered squashfs_symlink_readpage, page index %ld, start block " -+ "%llx, offset %x\n", page->index, -+ SQUASHFS_I(inode)->start_block, -+ SQUASHFS_I(inode)->offset); -+ -+ for (length = 0; length < index; length += bytes) { -+ if (!(bytes = squashfs_get_cached_block(inode->i_sb, NULL, -+ block, offset, PAGE_CACHE_SIZE, &block, -+ &offset))) { -+ ERROR("Unable to read symbolic link [%llx:%x]\n", block, -+ offset); -+ goto skip_read; -+ } -+ } -+ -+ if (length != index) { -+ ERROR("(squashfs_symlink_readpage) length != index\n"); -+ bytes = 0; -+ goto skip_read; -+ } -+ -+ bytes = (i_size_read(inode) - length) > PAGE_CACHE_SIZE ? PAGE_CACHE_SIZE : -+ i_size_read(inode) - length; -+ -+ if (!(bytes = squashfs_get_cached_block(inode->i_sb, pageaddr, block, -+ offset, bytes, &block, &offset))) -+ ERROR("Unable to read symbolic link [%llx:%x]\n", block, offset); -+ -+skip_read: -+ memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes); -+ kunmap(page); -+ SetPageUptodate(page); -+ unlock_page(page); -+ -+ return 0; -+} -+ -+ -+struct meta_index *locate_meta_index(struct inode *inode, int index, int offset) -+{ -+ struct meta_index *meta = NULL; -+ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; -+ int i; -+ -+ down(&msblk->meta_index_mutex); -+ -+ TRACE("locate_meta_index: index %d, offset %d\n", index, offset); -+ -+ if(msblk->meta_index == NULL) -+ goto not_allocated; -+ -+ for (i = 0; i < SQUASHFS_META_NUMBER; i ++) -+ if (msblk->meta_index[i].inode_number == inode->i_ino && -+ msblk->meta_index[i].offset >= offset && -+ msblk->meta_index[i].offset <= index && -+ msblk->meta_index[i].locked == 0) { -+ TRACE("locate_meta_index: entry %d, offset %d\n", i, -+ msblk->meta_index[i].offset); -+ meta = &msblk->meta_index[i]; -+ offset = meta->offset; -+ } -+ -+ if (meta) -+ meta->locked = 1; -+ -+not_allocated: -+ up(&msblk->meta_index_mutex); -+ -+ return meta; -+} -+ -+ -+struct meta_index *empty_meta_index(struct inode *inode, int offset, int skip) -+{ -+ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; -+ struct meta_index *meta = NULL; -+ int i; -+ -+ down(&msblk->meta_index_mutex); -+ -+ TRACE("empty_meta_index: offset %d, skip %d\n", offset, skip); -+ -+ if(msblk->meta_index == NULL) { -+ if (!(msblk->meta_index = kmalloc(sizeof(struct meta_index) * -+ SQUASHFS_META_NUMBER, GFP_KERNEL))) { -+ ERROR("Failed to allocate meta_index\n"); -+ goto failed; -+ } -+ for(i = 0; i < SQUASHFS_META_NUMBER; i++) { -+ msblk->meta_index[i].inode_number = 0; -+ msblk->meta_index[i].locked = 0; -+ } -+ msblk->next_meta_index = 0; -+ } -+ -+ for(i = SQUASHFS_META_NUMBER; i && -+ msblk->meta_index[msblk->next_meta_index].locked; i --) -+ msblk->next_meta_index = (msblk->next_meta_index + 1) % -+ SQUASHFS_META_NUMBER; -+ -+ if(i == 0) { -+ TRACE("empty_meta_index: failed!\n"); -+ goto failed; -+ } -+ -+ TRACE("empty_meta_index: returned meta entry %d, %p\n", -+ msblk->next_meta_index, -+ &msblk->meta_index[msblk->next_meta_index]); -+ -+ meta = &msblk->meta_index[msblk->next_meta_index]; -+ msblk->next_meta_index = (msblk->next_meta_index + 1) % -+ SQUASHFS_META_NUMBER; -+ -+ meta->inode_number = inode->i_ino; -+ meta->offset = offset; -+ meta->skip = skip; -+ meta->entries = 0; -+ meta->locked = 1; -+ -+failed: -+ up(&msblk->meta_index_mutex); -+ return meta; -+} -+ -+ -+void release_meta_index(struct inode *inode, struct meta_index *meta) -+{ -+ meta->locked = 0; -+} -+ -+ -+static int read_block_index(struct super_block *s, int blocks, char *block_list, -+ long long *start_block, int *offset) -+{ -+ struct squashfs_sb_info *msblk = s->s_fs_info; -+ unsigned int *block_listp; -+ int block = 0; -+ -+ if (msblk->swap) { -+ char sblock_list[blocks << 2]; -+ -+ if (!squashfs_get_cached_block(s, sblock_list, *start_block, -+ *offset, blocks << 2, start_block, offset)) { -+ ERROR("Unable to read block list [%llx:%x]\n", -+ *start_block, *offset); -+ goto failure; -+ } -+ SQUASHFS_SWAP_INTS(((unsigned int *)block_list), -+ ((unsigned int *)sblock_list), blocks); -+ } else -+ if (!squashfs_get_cached_block(s, block_list, *start_block, -+ *offset, blocks << 2, start_block, offset)) { -+ ERROR("Unable to read block list [%llx:%x]\n", -+ *start_block, *offset); -+ goto failure; -+ } -+ -+ for (block_listp = (unsigned int *) block_list; blocks; -+ block_listp++, blocks --) -+ block += SQUASHFS_COMPRESSED_SIZE_BLOCK(*block_listp); -+ -+ return block; -+ -+failure: -+ return -1; -+} -+ -+ -+#define SIZE 256 -+ -+static inline int calculate_skip(int blocks) { -+ int skip = (blocks - 1) / ((SQUASHFS_SLOTS * SQUASHFS_META_ENTRIES + 1) * SQUASHFS_META_INDEXES); -+ return skip >= 7 ? 7 : skip + 1; -+} -+ -+ -+static int get_meta_index(struct inode *inode, int index, -+ long long *index_block, int *index_offset, -+ long long *data_block, char *block_list) -+{ -+ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ int skip = calculate_skip(i_size_read(inode) >> sblk->block_log); -+ int offset = 0; -+ struct meta_index *meta; -+ struct meta_entry *meta_entry; -+ long long cur_index_block = SQUASHFS_I(inode)->u.s1.block_list_start; -+ int cur_offset = SQUASHFS_I(inode)->offset; -+ long long cur_data_block = SQUASHFS_I(inode)->start_block; -+ int i; -+ -+ index /= SQUASHFS_META_INDEXES * skip; -+ -+ while ( offset < index ) { -+ meta = locate_meta_index(inode, index, offset + 1); -+ -+ if (meta == NULL) { -+ if ((meta = empty_meta_index(inode, offset + 1, -+ skip)) == NULL) -+ goto all_done; -+ } else { -+ offset = index < meta->offset + meta->entries ? index : -+ meta->offset + meta->entries - 1; -+ meta_entry = &meta->meta_entry[offset - meta->offset]; -+ cur_index_block = meta_entry->index_block + sblk->inode_table_start; -+ cur_offset = meta_entry->offset; -+ cur_data_block = meta_entry->data_block; -+ TRACE("get_meta_index: offset %d, meta->offset %d, " -+ "meta->entries %d\n", offset, meta->offset, -+ meta->entries); -+ TRACE("get_meta_index: index_block 0x%llx, offset 0x%x" -+ " data_block 0x%llx\n", cur_index_block, -+ cur_offset, cur_data_block); -+ } -+ -+ for (i = meta->offset + meta->entries; i <= index && -+ i < meta->offset + SQUASHFS_META_ENTRIES; i++) { -+ int blocks = skip * SQUASHFS_META_INDEXES; -+ -+ while (blocks) { -+ int block = blocks > (SIZE >> 2) ? (SIZE >> 2) : -+ blocks; -+ int res = read_block_index(inode->i_sb, block, -+ block_list, &cur_index_block, -+ &cur_offset); -+ -+ if (res == -1) -+ goto failed; -+ -+ cur_data_block += res; -+ blocks -= block; -+ } -+ -+ meta_entry = &meta->meta_entry[i - meta->offset]; -+ meta_entry->index_block = cur_index_block - sblk->inode_table_start; -+ meta_entry->offset = cur_offset; -+ meta_entry->data_block = cur_data_block; -+ meta->entries ++; -+ offset ++; -+ } -+ -+ TRACE("get_meta_index: meta->offset %d, meta->entries %d\n", -+ meta->offset, meta->entries); -+ -+ release_meta_index(inode, meta); -+ } -+ -+all_done: -+ *index_block = cur_index_block; -+ *index_offset = cur_offset; -+ *data_block = cur_data_block; -+ -+ return offset * SQUASHFS_META_INDEXES * skip; -+ -+failed: -+ release_meta_index(inode, meta); -+ return -1; -+} -+ -+ -+static long long read_blocklist(struct inode *inode, int index, -+ int readahead_blks, char *block_list, -+ unsigned short **block_p, unsigned int *bsize) -+{ -+ long long block_ptr; -+ int offset; -+ long long block; -+ int res = get_meta_index(inode, index, &block_ptr, &offset, &block, -+ block_list); -+ -+ TRACE("read_blocklist: res %d, index %d, block_ptr 0x%llx, offset" -+ " 0x%x, block 0x%llx\n", res, index, block_ptr, offset, -+ block); -+ -+ if(res == -1) -+ goto failure; -+ -+ index -= res; -+ -+ while ( index ) { -+ int blocks = index > (SIZE >> 2) ? (SIZE >> 2) : index; -+ int res = read_block_index(inode->i_sb, blocks, block_list, -+ &block_ptr, &offset); -+ if (res == -1) -+ goto failure; -+ block += res; -+ index -= blocks; -+ } -+ -+ if (read_block_index(inode->i_sb, 1, block_list, -+ &block_ptr, &offset) == -1) -+ goto failure; -+ *bsize = *((unsigned int *) block_list); -+ -+ return block; -+ -+failure: -+ return 0; -+} -+ -+ -+static int squashfs_readpage(struct file *file, struct page *page) -+{ -+ struct inode *inode = page->mapping->host; -+ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ unsigned char block_list[SIZE]; -+ long long block; -+ unsigned int bsize, i = 0, bytes = 0, byte_offset = 0; -+ int index = page->index >> (sblk->block_log - PAGE_CACHE_SHIFT); -+ void *pageaddr; -+ struct squashfs_fragment_cache *fragment = NULL; -+ char *data_ptr = msblk->read_page; -+ -+ int mask = (1 << (sblk->block_log - PAGE_CACHE_SHIFT)) - 1; -+ int start_index = page->index & ~mask; -+ int end_index = start_index | mask; -+ -+ TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n", -+ page->index, -+ SQUASHFS_I(inode)->start_block); -+ -+ if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> -+ PAGE_CACHE_SHIFT)) -+ goto skip_read; -+ -+ if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK -+ || index < (i_size_read(inode) >> -+ sblk->block_log)) { -+ if ((block = (msblk->read_blocklist)(inode, index, 1, -+ block_list, NULL, &bsize)) == 0) -+ goto skip_read; -+ -+ down(&msblk->read_page_mutex); -+ -+ if (!(bytes = squashfs_read_data(inode->i_sb, msblk->read_page, -+ block, bsize, NULL))) { -+ ERROR("Unable to read page, block %llx, size %x\n", block, -+ bsize); -+ up(&msblk->read_page_mutex); -+ goto skip_read; -+ } -+ } else { -+ if ((fragment = get_cached_fragment(inode->i_sb, -+ SQUASHFS_I(inode)-> -+ u.s1.fragment_start_block, -+ SQUASHFS_I(inode)->u.s1.fragment_size)) -+ == NULL) { -+ ERROR("Unable to read page, block %llx, size %x\n", -+ SQUASHFS_I(inode)-> -+ u.s1.fragment_start_block, -+ (int) SQUASHFS_I(inode)-> -+ u.s1.fragment_size); -+ goto skip_read; -+ } -+ bytes = SQUASHFS_I(inode)->u.s1.fragment_offset + -+ (i_size_read(inode) & (sblk->block_size -+ - 1)); -+ byte_offset = SQUASHFS_I(inode)->u.s1.fragment_offset; -+ data_ptr = fragment->data; -+ } -+ -+ for (i = start_index; i <= end_index && byte_offset < bytes; -+ i++, byte_offset += PAGE_CACHE_SIZE) { -+ struct page *push_page; -+ int available_bytes = (bytes - byte_offset) > PAGE_CACHE_SIZE ? -+ PAGE_CACHE_SIZE : bytes - byte_offset; -+ -+ TRACE("bytes %d, i %d, byte_offset %d, available_bytes %d\n", -+ bytes, i, byte_offset, available_bytes); -+ -+ if (i == page->index) { -+ pageaddr = kmap_atomic(page, KM_USER0); -+ memcpy(pageaddr, data_ptr + byte_offset, -+ available_bytes); -+ memset(pageaddr + available_bytes, 0, -+ PAGE_CACHE_SIZE - available_bytes); -+ kunmap_atomic(pageaddr, KM_USER0); -+ flush_dcache_page(page); -+ SetPageUptodate(page); -+ unlock_page(page); -+ } else if ((push_page = -+ grab_cache_page_nowait(page->mapping, i))) { -+ pageaddr = kmap_atomic(push_page, KM_USER0); -+ -+ memcpy(pageaddr, data_ptr + byte_offset, -+ available_bytes); -+ memset(pageaddr + available_bytes, 0, -+ PAGE_CACHE_SIZE - available_bytes); -+ kunmap_atomic(pageaddr, KM_USER0); -+ flush_dcache_page(push_page); -+ SetPageUptodate(push_page); -+ unlock_page(push_page); -+ page_cache_release(push_page); -+ } -+ } -+ -+ if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK -+ || index < (i_size_read(inode) >> -+ sblk->block_log)) -+ up(&msblk->read_page_mutex); -+ else -+ release_cached_fragment(msblk, fragment); -+ -+ return 0; -+ -+skip_read: -+ pageaddr = kmap_atomic(page, KM_USER0); -+ memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes); -+ kunmap_atomic(pageaddr, KM_USER0); -+ flush_dcache_page(page); -+ SetPageUptodate(page); -+ unlock_page(page); -+ -+ return 0; -+} -+ -+ -+static int squashfs_readpage4K(struct file *file, struct page *page) -+{ -+ struct inode *inode = page->mapping->host; -+ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ unsigned char block_list[SIZE]; -+ long long block; -+ unsigned int bsize, bytes = 0; -+ void *pageaddr; -+ -+ TRACE("Entered squashfs_readpage4K, page index %lx, start block %llx\n", -+ page->index, -+ SQUASHFS_I(inode)->start_block); -+ -+ if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> -+ PAGE_CACHE_SHIFT)) { -+ pageaddr = kmap_atomic(page, KM_USER0); -+ goto skip_read; -+ } -+ -+ if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK -+ || page->index < (i_size_read(inode) >> -+ sblk->block_log)) { -+ block = (msblk->read_blocklist)(inode, page->index, 1, -+ block_list, NULL, &bsize); -+ -+ down(&msblk->read_page_mutex); -+ bytes = squashfs_read_data(inode->i_sb, msblk->read_page, block, -+ bsize, NULL); -+ pageaddr = kmap_atomic(page, KM_USER0); -+ if (bytes) -+ memcpy(pageaddr, msblk->read_page, bytes); -+ else -+ ERROR("Unable to read page, block %llx, size %x\n", -+ block, bsize); -+ up(&msblk->read_page_mutex); -+ } else { -+ struct squashfs_fragment_cache *fragment = -+ get_cached_fragment(inode->i_sb, -+ SQUASHFS_I(inode)-> -+ u.s1.fragment_start_block, -+ SQUASHFS_I(inode)-> u.s1.fragment_size); -+ pageaddr = kmap_atomic(page, KM_USER0); -+ if (fragment) { -+ bytes = i_size_read(inode) & (sblk->block_size - 1); -+ memcpy(pageaddr, fragment->data + SQUASHFS_I(inode)-> -+ u.s1.fragment_offset, bytes); -+ release_cached_fragment(msblk, fragment); -+ } else -+ ERROR("Unable to read page, block %llx, size %x\n", -+ SQUASHFS_I(inode)-> -+ u.s1.fragment_start_block, (int) -+ SQUASHFS_I(inode)-> u.s1.fragment_size); -+ } -+ -+skip_read: -+ memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes); -+ kunmap_atomic(pageaddr, KM_USER0); -+ flush_dcache_page(page); -+ SetPageUptodate(page); -+ unlock_page(page); -+ -+ return 0; -+} -+ -+ -+static int get_dir_index_using_offset(struct super_block *s, long long -+ *next_block, unsigned int *next_offset, -+ long long index_start, -+ unsigned int index_offset, int i_count, -+ long long f_pos) -+{ -+ struct squashfs_sb_info *msblk = s->s_fs_info; -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ int i, length = 0; -+ struct squashfs_dir_index index; -+ -+ TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %d\n", -+ i_count, (unsigned int) f_pos); -+ -+ f_pos =- 3; -+ if (f_pos == 0) -+ goto finish; -+ -+ for (i = 0; i < i_count; i++) { -+ if (msblk->swap) { -+ struct squashfs_dir_index sindex; -+ squashfs_get_cached_block(s, (char *) &sindex, -+ index_start, index_offset, -+ sizeof(sindex), &index_start, -+ &index_offset); -+ SQUASHFS_SWAP_DIR_INDEX(&index, &sindex); -+ } else -+ squashfs_get_cached_block(s, (char *) &index, -+ index_start, index_offset, -+ sizeof(index), &index_start, -+ &index_offset); -+ -+ if (index.index > f_pos) -+ break; -+ -+ squashfs_get_cached_block(s, NULL, index_start, index_offset, -+ index.size + 1, &index_start, -+ &index_offset); -+ -+ length = index.index; -+ *next_block = index.start_block + sblk->directory_table_start; -+ } -+ -+ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE; -+ -+finish: -+ return length + 3; -+} -+ -+ -+static int get_dir_index_using_name(struct super_block *s, long long -+ *next_block, unsigned int *next_offset, -+ long long index_start, -+ unsigned int index_offset, int i_count, -+ const char *name, int size) -+{ -+ struct squashfs_sb_info *msblk = s->s_fs_info; -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ int i, length = 0; -+ char buffer[sizeof(struct squashfs_dir_index) + SQUASHFS_NAME_LEN + 1]; -+ struct squashfs_dir_index *index = (struct squashfs_dir_index *) buffer; -+ char str[SQUASHFS_NAME_LEN + 1]; -+ -+ TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count); -+ -+ strncpy(str, name, size); -+ str[size] = '\0'; -+ -+ for (i = 0; i < i_count; i++) { -+ if (msblk->swap) { -+ struct squashfs_dir_index sindex; -+ squashfs_get_cached_block(s, (char *) &sindex, -+ index_start, index_offset, -+ sizeof(sindex), &index_start, -+ &index_offset); -+ SQUASHFS_SWAP_DIR_INDEX(index, &sindex); -+ } else -+ squashfs_get_cached_block(s, (char *) index, -+ index_start, index_offset, -+ sizeof(struct squashfs_dir_index), -+ &index_start, &index_offset); -+ -+ squashfs_get_cached_block(s, index->name, index_start, -+ index_offset, index->size + 1, -+ &index_start, &index_offset); -+ -+ index->name[index->size + 1] = '\0'; -+ -+ if (strcmp(index->name, str) > 0) -+ break; -+ -+ length = index->index; -+ *next_block = index->start_block + sblk->directory_table_start; -+ } -+ -+ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE; -+ return length + 3; -+} -+ -+ -+static int squashfs_readdir(struct file *file, void *dirent, filldir_t filldir) -+{ -+ struct inode *i = file->f_dentry->d_inode; -+ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info; -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ long long next_block = SQUASHFS_I(i)->start_block + -+ sblk->directory_table_start; -+ int next_offset = SQUASHFS_I(i)->offset, length = 0, dirs_read = 0, -+ dir_count; -+ struct squashfs_dir_header dirh; -+ char buffer[sizeof(struct squashfs_dir_entry) + SQUASHFS_NAME_LEN + 1]; -+ struct squashfs_dir_entry *dire = (struct squashfs_dir_entry *) buffer; -+ -+ TRACE("Entered squashfs_readdir [%llx:%x]\n", next_block, next_offset); -+ -+ while(file->f_pos < 3) { -+ char *name; -+ int size, i_ino; -+ -+ if(file->f_pos == 0) { -+ name = "."; -+ size = 1; -+ i_ino = i->i_ino; -+ } else { -+ name = ".."; -+ size = 2; -+ i_ino = SQUASHFS_I(i)->u.s2.parent_inode; -+ } -+ TRACE("Calling filldir(%x, %s, %d, %d, %d, %d)\n", -+ (unsigned int) dirent, name, size, (int) -+ file->f_pos, i_ino, -+ squashfs_filetype_table[1]); -+ -+ if (filldir(dirent, name, size, -+ file->f_pos, i_ino, -+ squashfs_filetype_table[1]) < 0) { -+ TRACE("Filldir returned less than 0\n"); -+ goto finish; -+ } -+ file->f_pos += size; -+ dirs_read++; -+ } -+ -+ length = get_dir_index_using_offset(i->i_sb, &next_block, &next_offset, -+ SQUASHFS_I(i)->u.s2.directory_index_start, -+ SQUASHFS_I(i)->u.s2.directory_index_offset, -+ SQUASHFS_I(i)->u.s2.directory_index_count, -+ file->f_pos); -+ -+ while (length < i_size_read(i)) { -+ /* read directory header */ -+ if (msblk->swap) { -+ struct squashfs_dir_header sdirh; -+ -+ if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh, -+ next_block, next_offset, sizeof(sdirh), -+ &next_block, &next_offset)) -+ goto failed_read; -+ -+ length += sizeof(sdirh); -+ SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh); -+ } else { -+ if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh, -+ next_block, next_offset, sizeof(dirh), -+ &next_block, &next_offset)) -+ goto failed_read; -+ -+ length += sizeof(dirh); -+ } -+ -+ dir_count = dirh.count + 1; -+ while (dir_count--) { -+ if (msblk->swap) { -+ struct squashfs_dir_entry sdire; -+ if (!squashfs_get_cached_block(i->i_sb, (char *) -+ &sdire, next_block, next_offset, -+ sizeof(sdire), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ length += sizeof(sdire); -+ SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire); -+ } else { -+ if (!squashfs_get_cached_block(i->i_sb, (char *) -+ dire, next_block, next_offset, -+ sizeof(*dire), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ length += sizeof(*dire); -+ } -+ -+ if (!squashfs_get_cached_block(i->i_sb, dire->name, -+ next_block, next_offset, -+ dire->size + 1, &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ length += dire->size + 1; -+ -+ if (file->f_pos >= length) -+ continue; -+ -+ dire->name[dire->size + 1] = '\0'; -+ -+ TRACE("Calling filldir(%x, %s, %d, %d, %x:%x, %d, %d)\n", -+ (unsigned int) dirent, dire->name, -+ dire->size + 1, (int) file->f_pos, -+ dirh.start_block, dire->offset, -+ dirh.inode_number + dire->inode_number, -+ squashfs_filetype_table[dire->type]); -+ -+ if (filldir(dirent, dire->name, dire->size + 1, -+ file->f_pos, -+ dirh.inode_number + dire->inode_number, -+ squashfs_filetype_table[dire->type]) -+ < 0) { -+ TRACE("Filldir returned less than 0\n"); -+ goto finish; -+ } -+ file->f_pos = length; -+ dirs_read++; -+ } -+ } -+ -+finish: -+ return dirs_read; -+ -+failed_read: -+ ERROR("Unable to read directory block [%llx:%x]\n", next_block, -+ next_offset); -+ return 0; -+} -+ -+ -+static struct dentry *squashfs_lookup(struct inode *i, struct dentry *dentry, -+ struct nameidata *nd) -+{ -+ const unsigned char *name = dentry->d_name.name; -+ int len = dentry->d_name.len; -+ struct inode *inode = NULL; -+ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info; -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ long long next_block = SQUASHFS_I(i)->start_block + -+ sblk->directory_table_start; -+ int next_offset = SQUASHFS_I(i)->offset, length = 0, -+ dir_count; -+ struct squashfs_dir_header dirh; -+ char buffer[sizeof(struct squashfs_dir_entry) + SQUASHFS_NAME_LEN]; -+ struct squashfs_dir_entry *dire = (struct squashfs_dir_entry *) buffer; -+ -+ TRACE("Entered squashfs_lookup [%llx:%x]\n", next_block, next_offset); -+ -+ if (len > SQUASHFS_NAME_LEN) -+ goto exit_loop; -+ -+ length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset, -+ SQUASHFS_I(i)->u.s2.directory_index_start, -+ SQUASHFS_I(i)->u.s2.directory_index_offset, -+ SQUASHFS_I(i)->u.s2.directory_index_count, name, -+ len); -+ -+ while (length < i_size_read(i)) { -+ /* read directory header */ -+ if (msblk->swap) { -+ struct squashfs_dir_header sdirh; -+ if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh, -+ next_block, next_offset, sizeof(sdirh), -+ &next_block, &next_offset)) -+ goto failed_read; -+ -+ length += sizeof(sdirh); -+ SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh); -+ } else { -+ if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh, -+ next_block, next_offset, sizeof(dirh), -+ &next_block, &next_offset)) -+ goto failed_read; -+ -+ length += sizeof(dirh); -+ } -+ -+ dir_count = dirh.count + 1; -+ while (dir_count--) { -+ if (msblk->swap) { -+ struct squashfs_dir_entry sdire; -+ if (!squashfs_get_cached_block(i->i_sb, (char *) -+ &sdire, next_block,next_offset, -+ sizeof(sdire), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ length += sizeof(sdire); -+ SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire); -+ } else { -+ if (!squashfs_get_cached_block(i->i_sb, (char *) -+ dire, next_block,next_offset, -+ sizeof(*dire), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ length += sizeof(*dire); -+ } -+ -+ if (!squashfs_get_cached_block(i->i_sb, dire->name, -+ next_block, next_offset, dire->size + 1, -+ &next_block, &next_offset)) -+ goto failed_read; -+ -+ length += dire->size + 1; -+ -+ if (name[0] < dire->name[0]) -+ goto exit_loop; -+ -+ if ((len == dire->size + 1) && !strncmp(name, -+ dire->name, len)) { -+ squashfs_inode_t ino = -+ SQUASHFS_MKINODE(dirh.start_block, -+ dire->offset); -+ -+ TRACE("calling squashfs_iget for directory " -+ "entry %s, inode %x:%x, %d\n", name, -+ dirh.start_block, dire->offset, -+ dirh.inode_number + dire->inode_number); -+ -+ inode = (msblk->iget)(i->i_sb, ino); -+ -+ goto exit_loop; -+ } -+ } -+ } -+ -+exit_loop: -+ d_add(dentry, inode); -+ return ERR_PTR(0); -+ -+failed_read: -+ ERROR("Unable to read directory block [%llx:%x]\n", next_block, -+ next_offset); -+ goto exit_loop; -+} -+ -+ -+static void squashfs_put_super(struct super_block *s) -+{ -+ int i; -+ -+ if (s->s_fs_info) { -+ struct squashfs_sb_info *sbi = s->s_fs_info; -+ if (sbi->block_cache) -+ for (i = 0; i < SQUASHFS_CACHED_BLKS; i++) -+ if (sbi->block_cache[i].block != -+ SQUASHFS_INVALID_BLK) -+ kfree(sbi->block_cache[i].data); -+ if (sbi->fragment) -+ for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++) -+ SQUASHFS_FREE(sbi->fragment[i].data); -+ kfree(sbi->fragment); -+ kfree(sbi->block_cache); -+ kfree(sbi->read_data); -+ kfree(sbi->read_page); -+ kfree(sbi->uid); -+ kfree(sbi->fragment_index); -+ kfree(sbi->fragment_index_2); -+ kfree(sbi->meta_index); -+ kfree(s->s_fs_info); -+ s->s_fs_info = NULL; -+ } -+} -+ -+ -+static int squashfs_get_sb(struct file_system_type *fs_type, -+ int flags, const char *dev_name, void *data, struct vfsmount *mnt) -+{ -+ return get_sb_bdev(fs_type, flags, dev_name, data, squashfs_fill_super, mnt); -+} -+ -+ -+static int __init init_squashfs_fs(void) -+{ -+ int err = init_inodecache(); -+ if (err) -+ goto out; -+ -+ printk(KERN_INFO "squashfs: version 3.0 (2006/03/15) " -+ "Phillip Lougher\n"); -+ -+ if (!(stream.workspace = vmalloc(zlib_inflate_workspacesize()))) { -+ ERROR("Failed to allocate zlib workspace\n"); -+ destroy_inodecache(); -+ err = -ENOMEM; -+ goto out; -+ } -+ -+ if ((err = register_filesystem(&squashfs_fs_type))) { -+ vfree(stream.workspace); -+ destroy_inodecache(); -+ } -+ -+out: -+ return err; -+} -+ -+ -+static void __exit exit_squashfs_fs(void) -+{ -+ vfree(stream.workspace); -+ unregister_filesystem(&squashfs_fs_type); -+ destroy_inodecache(); -+} -+ -+ -+static struct kmem_cache* squashfs_inode_cachep; -+ -+ -+static struct inode *squashfs_alloc_inode(struct super_block *sb) -+{ -+ struct squashfs_inode_info *ei; -+ ei = kmem_cache_alloc(squashfs_inode_cachep, GFP_KERNEL); -+ if (!ei) -+ return NULL; -+ return &ei->vfs_inode; -+} -+ -+ -+static void squashfs_destroy_inode(struct inode *inode) -+{ -+ kmem_cache_free(squashfs_inode_cachep, SQUASHFS_I(inode)); -+} -+ -+ -+static void init_once(void * foo, struct kmem_cache *cachep, unsigned long flags) -+{ -+ struct squashfs_inode_info *ei = foo; -+ -+ inode_init_once(&ei->vfs_inode); -+} -+ -+ -+static int __init init_inodecache(void) -+{ -+ squashfs_inode_cachep = kmem_cache_create("squashfs_inode_cache", -+ sizeof(struct squashfs_inode_info), -+ 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, -+ init_once); -+ if (squashfs_inode_cachep == NULL) -+ return -ENOMEM; -+ return 0; -+} -+ -+ -+static void destroy_inodecache(void) -+{ -+ kmem_cache_destroy(squashfs_inode_cachep); -+} -+ -+ -+module_init(init_squashfs_fs); -+module_exit(exit_squashfs_fs); -+MODULE_DESCRIPTION("squashfs, a compressed read-only filesystem"); -+MODULE_AUTHOR("Phillip Lougher <phillip@lougher.org.uk>"); -Index: linux-2.6.22/fs/squashfs/Makefile -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/fs/squashfs/Makefile 2007-08-28 21:56:34.000000000 +0100 -@@ -0,0 +1,7 @@ -+# -+# Makefile for the linux squashfs routines. -+# -+ -+obj-$(CONFIG_SQUASHFS) += squashfs.o -+squashfs-y += inode.o -+squashfs-y += squashfs2_0.o -Index: linux-2.6.22/fs/squashfs/squashfs2_0.c -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/fs/squashfs/squashfs2_0.c 2007-08-28 21:56:34.000000000 +0100 -@@ -0,0 +1,757 @@ -+/* -+ * Squashfs - a compressed read only filesystem for Linux -+ * -+ * Copyright (c) 2002, 2003, 2004, 2005, 2006 -+ * Phillip Lougher <phillip@lougher.org.uk> -+ * -+ * 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 the Free Software Foundation; either version 2, -+ * or (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -+ * -+ * squashfs2_0.c -+ */ -+ -+#include <linux/types.h> -+#include <linux/squashfs_fs.h> -+#include <linux/module.h> -+#include <linux/errno.h> -+#include <linux/slab.h> -+#include <linux/fs.h> -+#include <linux/smp_lock.h> -+#include <linux/slab.h> -+#include <linux/squashfs_fs_sb.h> -+#include <linux/squashfs_fs_i.h> -+#include <linux/buffer_head.h> -+#include <linux/vfs.h> -+#include <linux/init.h> -+#include <linux/dcache.h> -+#include <linux/wait.h> -+#include <linux/zlib.h> -+#include <linux/blkdev.h> -+#include <linux/vmalloc.h> -+#include <asm/uaccess.h> -+#include <asm/semaphore.h> -+ -+#include "squashfs.h" -+static int squashfs_readdir_2(struct file *file, void *dirent, filldir_t filldir); -+static struct dentry *squashfs_lookup_2(struct inode *, struct dentry *, -+ struct nameidata *); -+ -+static struct file_operations squashfs_dir_ops_2 = { -+ .read = generic_read_dir, -+ .readdir = squashfs_readdir_2 -+}; -+ -+static struct inode_operations squashfs_dir_inode_ops_2 = { -+ .lookup = squashfs_lookup_2 -+}; -+ -+static unsigned char squashfs_filetype_table[] = { -+ DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK -+}; -+ -+static int read_fragment_index_table_2(struct super_block *s) -+{ -+ struct squashfs_sb_info *msblk = s->s_fs_info; -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ -+ if (!(msblk->fragment_index_2 = kmalloc(SQUASHFS_FRAGMENT_INDEX_BYTES_2 -+ (sblk->fragments), GFP_KERNEL))) { -+ ERROR("Failed to allocate uid/gid table\n"); -+ return 0; -+ } -+ -+ if (SQUASHFS_FRAGMENT_INDEX_BYTES_2(sblk->fragments) && -+ !squashfs_read_data(s, (char *) -+ msblk->fragment_index_2, -+ sblk->fragment_table_start, -+ SQUASHFS_FRAGMENT_INDEX_BYTES_2 -+ (sblk->fragments) | -+ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { -+ ERROR("unable to read fragment index table\n"); -+ return 0; -+ } -+ -+ if (msblk->swap) { -+ int i; -+ unsigned int fragment; -+ -+ for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES_2(sblk->fragments); -+ i++) { -+ SQUASHFS_SWAP_FRAGMENT_INDEXES_2((&fragment), -+ &msblk->fragment_index_2[i], 1); -+ msblk->fragment_index_2[i] = fragment; -+ } -+ } -+ -+ return 1; -+} -+ -+ -+static int get_fragment_location_2(struct super_block *s, unsigned int fragment, -+ long long *fragment_start_block, -+ unsigned int *fragment_size) -+{ -+ struct squashfs_sb_info *msblk = s->s_fs_info; -+ long long start_block = -+ msblk->fragment_index_2[SQUASHFS_FRAGMENT_INDEX_2(fragment)]; -+ int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET_2(fragment); -+ struct squashfs_fragment_entry_2 fragment_entry; -+ -+ if (msblk->swap) { -+ struct squashfs_fragment_entry_2 sfragment_entry; -+ -+ if (!squashfs_get_cached_block(s, (char *) &sfragment_entry, -+ start_block, offset, -+ sizeof(sfragment_entry), &start_block, -+ &offset)) -+ goto out; -+ SQUASHFS_SWAP_FRAGMENT_ENTRY_2(&fragment_entry, &sfragment_entry); -+ } else -+ if (!squashfs_get_cached_block(s, (char *) &fragment_entry, -+ start_block, offset, -+ sizeof(fragment_entry), &start_block, -+ &offset)) -+ goto out; -+ -+ *fragment_start_block = fragment_entry.start_block; -+ *fragment_size = fragment_entry.size; -+ -+ return 1; -+ -+out: -+ return 0; -+} -+ -+ -+static struct inode *squashfs_new_inode(struct super_block *s, -+ struct squashfs_base_inode_header_2 *inodeb, unsigned int ino) -+{ -+ struct squashfs_sb_info *msblk = s->s_fs_info; -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ struct inode *i = new_inode(s); -+ -+ if (i) { -+ i->i_ino = ino; -+ i->i_mtime.tv_sec = sblk->mkfs_time; -+ i->i_atime.tv_sec = sblk->mkfs_time; -+ i->i_ctime.tv_sec = sblk->mkfs_time; -+ i->i_uid = msblk->uid[inodeb->uid]; -+ i->i_mode = inodeb->mode; -+ i->i_nlink = 1; -+ i->i_size = 0; -+ if (inodeb->guid == SQUASHFS_GUIDS) -+ i->i_gid = i->i_uid; -+ else -+ i->i_gid = msblk->guid[inodeb->guid]; -+ } -+ -+ return i; -+} -+ -+ -+static struct inode *squashfs_iget_2(struct super_block *s, squashfs_inode_t inode) -+{ -+ struct inode *i; -+ struct squashfs_sb_info *msblk = s->s_fs_info; -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ unsigned int block = SQUASHFS_INODE_BLK(inode) + -+ sblk->inode_table_start; -+ unsigned int offset = SQUASHFS_INODE_OFFSET(inode); -+ unsigned int ino = SQUASHFS_MK_VFS_INODE(block -+ - sblk->inode_table_start, offset); -+ long long next_block; -+ unsigned int next_offset; -+ union squashfs_inode_header_2 id, sid; -+ struct squashfs_base_inode_header_2 *inodeb = &id.base, -+ *sinodeb = &sid.base; -+ -+ TRACE("Entered squashfs_iget\n"); -+ -+ if (msblk->swap) { -+ if (!squashfs_get_cached_block(s, (char *) sinodeb, block, -+ offset, sizeof(*sinodeb), &next_block, -+ &next_offset)) -+ goto failed_read; -+ SQUASHFS_SWAP_BASE_INODE_HEADER_2(inodeb, sinodeb, -+ sizeof(*sinodeb)); -+ } else -+ if (!squashfs_get_cached_block(s, (char *) inodeb, block, -+ offset, sizeof(*inodeb), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ switch(inodeb->inode_type) { -+ case SQUASHFS_FILE_TYPE: { -+ struct squashfs_reg_inode_header_2 *inodep = &id.reg; -+ struct squashfs_reg_inode_header_2 *sinodep = &sid.reg; -+ long long frag_blk; -+ unsigned int frag_size; -+ -+ if (msblk->swap) { -+ if (!squashfs_get_cached_block(s, (char *) -+ sinodep, block, offset, -+ sizeof(*sinodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ SQUASHFS_SWAP_REG_INODE_HEADER_2(inodep, sinodep); -+ } else -+ if (!squashfs_get_cached_block(s, (char *) -+ inodep, block, offset, -+ sizeof(*inodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ frag_blk = SQUASHFS_INVALID_BLK; -+ if (inodep->fragment != SQUASHFS_INVALID_FRAG && -+ !get_fragment_location_2(s, -+ inodep->fragment, &frag_blk, &frag_size)) -+ goto failed_read; -+ -+ if((i = squashfs_new_inode(s, inodeb, ino)) == NULL) -+ goto failed_read1; -+ -+ i->i_size = inodep->file_size; -+ i->i_fop = &generic_ro_fops; -+ i->i_mode |= S_IFREG; -+ i->i_mtime.tv_sec = inodep->mtime; -+ i->i_atime.tv_sec = inodep->mtime; -+ i->i_ctime.tv_sec = inodep->mtime; -+ i->i_blocks = ((i->i_size - 1) >> 9) + 1; -+ SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk; -+ SQUASHFS_I(i)->u.s1.fragment_size = frag_size; -+ SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset; -+ SQUASHFS_I(i)->start_block = inodep->start_block; -+ SQUASHFS_I(i)->u.s1.block_list_start = next_block; -+ SQUASHFS_I(i)->offset = next_offset; -+ if (sblk->block_size > 4096) -+ i->i_data.a_ops = &squashfs_aops; -+ else -+ i->i_data.a_ops = &squashfs_aops_4K; -+ -+ TRACE("File inode %x:%x, start_block %x, " -+ "block_list_start %llx, offset %x\n", -+ SQUASHFS_INODE_BLK(inode), offset, -+ inodep->start_block, next_block, -+ next_offset); -+ break; -+ } -+ case SQUASHFS_DIR_TYPE: { -+ struct squashfs_dir_inode_header_2 *inodep = &id.dir; -+ struct squashfs_dir_inode_header_2 *sinodep = &sid.dir; -+ -+ if (msblk->swap) { -+ if (!squashfs_get_cached_block(s, (char *) -+ sinodep, block, offset, -+ sizeof(*sinodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ SQUASHFS_SWAP_DIR_INODE_HEADER_2(inodep, sinodep); -+ } else -+ if (!squashfs_get_cached_block(s, (char *) -+ inodep, block, offset, -+ sizeof(*inodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ if((i = squashfs_new_inode(s, inodeb, ino)) == NULL) -+ goto failed_read1; -+ -+ i->i_size = inodep->file_size; -+ i->i_op = &squashfs_dir_inode_ops_2; -+ i->i_fop = &squashfs_dir_ops_2; -+ i->i_mode |= S_IFDIR; -+ i->i_mtime.tv_sec = inodep->mtime; -+ i->i_atime.tv_sec = inodep->mtime; -+ i->i_ctime.tv_sec = inodep->mtime; -+ SQUASHFS_I(i)->start_block = inodep->start_block; -+ SQUASHFS_I(i)->offset = inodep->offset; -+ SQUASHFS_I(i)->u.s2.directory_index_count = 0; -+ SQUASHFS_I(i)->u.s2.parent_inode = 0; -+ -+ TRACE("Directory inode %x:%x, start_block %x, offset " -+ "%x\n", SQUASHFS_INODE_BLK(inode), -+ offset, inodep->start_block, -+ inodep->offset); -+ break; -+ } -+ case SQUASHFS_LDIR_TYPE: { -+ struct squashfs_ldir_inode_header_2 *inodep = &id.ldir; -+ struct squashfs_ldir_inode_header_2 *sinodep = &sid.ldir; -+ -+ if (msblk->swap) { -+ if (!squashfs_get_cached_block(s, (char *) -+ sinodep, block, offset, -+ sizeof(*sinodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ SQUASHFS_SWAP_LDIR_INODE_HEADER_2(inodep, -+ sinodep); -+ } else -+ if (!squashfs_get_cached_block(s, (char *) -+ inodep, block, offset, -+ sizeof(*inodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ if((i = squashfs_new_inode(s, inodeb, ino)) == NULL) -+ goto failed_read1; -+ -+ i->i_size = inodep->file_size; -+ i->i_op = &squashfs_dir_inode_ops_2; -+ i->i_fop = &squashfs_dir_ops_2; -+ i->i_mode |= S_IFDIR; -+ i->i_mtime.tv_sec = inodep->mtime; -+ i->i_atime.tv_sec = inodep->mtime; -+ i->i_ctime.tv_sec = inodep->mtime; -+ SQUASHFS_I(i)->start_block = inodep->start_block; -+ SQUASHFS_I(i)->offset = inodep->offset; -+ SQUASHFS_I(i)->u.s2.directory_index_start = next_block; -+ SQUASHFS_I(i)->u.s2.directory_index_offset = -+ next_offset; -+ SQUASHFS_I(i)->u.s2.directory_index_count = -+ inodep->i_count; -+ SQUASHFS_I(i)->u.s2.parent_inode = 0; -+ -+ TRACE("Long directory inode %x:%x, start_block %x, " -+ "offset %x\n", -+ SQUASHFS_INODE_BLK(inode), offset, -+ inodep->start_block, inodep->offset); -+ break; -+ } -+ case SQUASHFS_SYMLINK_TYPE: { -+ struct squashfs_symlink_inode_header_2 *inodep = -+ &id.symlink; -+ struct squashfs_symlink_inode_header_2 *sinodep = -+ &sid.symlink; -+ -+ if (msblk->swap) { -+ if (!squashfs_get_cached_block(s, (char *) -+ sinodep, block, offset, -+ sizeof(*sinodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(inodep, -+ sinodep); -+ } else -+ if (!squashfs_get_cached_block(s, (char *) -+ inodep, block, offset, -+ sizeof(*inodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ if((i = squashfs_new_inode(s, inodeb, ino)) == NULL) -+ goto failed_read1; -+ -+ i->i_size = inodep->symlink_size; -+ i->i_op = &page_symlink_inode_operations; -+ i->i_data.a_ops = &squashfs_symlink_aops; -+ i->i_mode |= S_IFLNK; -+ SQUASHFS_I(i)->start_block = next_block; -+ SQUASHFS_I(i)->offset = next_offset; -+ -+ TRACE("Symbolic link inode %x:%x, start_block %llx, " -+ "offset %x\n", -+ SQUASHFS_INODE_BLK(inode), offset, -+ next_block, next_offset); -+ break; -+ } -+ case SQUASHFS_BLKDEV_TYPE: -+ case SQUASHFS_CHRDEV_TYPE: { -+ struct squashfs_dev_inode_header_2 *inodep = &id.dev; -+ struct squashfs_dev_inode_header_2 *sinodep = &sid.dev; -+ -+ if (msblk->swap) { -+ if (!squashfs_get_cached_block(s, (char *) -+ sinodep, block, offset, -+ sizeof(*sinodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ SQUASHFS_SWAP_DEV_INODE_HEADER_2(inodep, sinodep); -+ } else -+ if (!squashfs_get_cached_block(s, (char *) -+ inodep, block, offset, -+ sizeof(*inodep), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ if ((i = squashfs_new_inode(s, inodeb, ino)) == NULL) -+ goto failed_read1; -+ -+ i->i_mode |= (inodeb->inode_type == -+ SQUASHFS_CHRDEV_TYPE) ? S_IFCHR : -+ S_IFBLK; -+ init_special_inode(i, i->i_mode, -+ old_decode_dev(inodep->rdev)); -+ -+ TRACE("Device inode %x:%x, rdev %x\n", -+ SQUASHFS_INODE_BLK(inode), offset, -+ inodep->rdev); -+ break; -+ } -+ case SQUASHFS_FIFO_TYPE: -+ case SQUASHFS_SOCKET_TYPE: { -+ if ((i = squashfs_new_inode(s, inodeb, ino)) == NULL) -+ goto failed_read1; -+ -+ i->i_mode |= (inodeb->inode_type == SQUASHFS_FIFO_TYPE) -+ ? S_IFIFO : S_IFSOCK; -+ init_special_inode(i, i->i_mode, 0); -+ break; -+ } -+ default: -+ ERROR("Unknown inode type %d in squashfs_iget!\n", -+ inodeb->inode_type); -+ goto failed_read1; -+ } -+ -+ insert_inode_hash(i); -+ return i; -+ -+failed_read: -+ ERROR("Unable to read inode [%x:%x]\n", block, offset); -+ -+failed_read1: -+ return NULL; -+} -+ -+ -+static int get_dir_index_using_offset(struct super_block *s, long long -+ *next_block, unsigned int *next_offset, -+ long long index_start, -+ unsigned int index_offset, int i_count, -+ long long f_pos) -+{ -+ struct squashfs_sb_info *msblk = s->s_fs_info; -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ int i, length = 0; -+ struct squashfs_dir_index_2 index; -+ -+ TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %d\n", -+ i_count, (unsigned int) f_pos); -+ -+ if (f_pos == 0) -+ goto finish; -+ -+ for (i = 0; i < i_count; i++) { -+ if (msblk->swap) { -+ struct squashfs_dir_index_2 sindex; -+ squashfs_get_cached_block(s, (char *) &sindex, -+ index_start, index_offset, -+ sizeof(sindex), &index_start, -+ &index_offset); -+ SQUASHFS_SWAP_DIR_INDEX_2(&index, &sindex); -+ } else -+ squashfs_get_cached_block(s, (char *) &index, -+ index_start, index_offset, -+ sizeof(index), &index_start, -+ &index_offset); -+ -+ if (index.index > f_pos) -+ break; -+ -+ squashfs_get_cached_block(s, NULL, index_start, index_offset, -+ index.size + 1, &index_start, -+ &index_offset); -+ -+ length = index.index; -+ *next_block = index.start_block + sblk->directory_table_start; -+ } -+ -+ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE; -+ -+finish: -+ return length; -+} -+ -+ -+static int get_dir_index_using_name(struct super_block *s, long long -+ *next_block, unsigned int *next_offset, -+ long long index_start, -+ unsigned int index_offset, int i_count, -+ const char *name, int size) -+{ -+ struct squashfs_sb_info *msblk = s->s_fs_info; -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ int i, length = 0; -+ char buffer[sizeof(struct squashfs_dir_index_2) + SQUASHFS_NAME_LEN + 1]; -+ struct squashfs_dir_index_2 *index = (struct squashfs_dir_index_2 *) buffer; -+ char str[SQUASHFS_NAME_LEN + 1]; -+ -+ TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count); -+ -+ strncpy(str, name, size); -+ str[size] = '\0'; -+ -+ for (i = 0; i < i_count; i++) { -+ if (msblk->swap) { -+ struct squashfs_dir_index_2 sindex; -+ squashfs_get_cached_block(s, (char *) &sindex, -+ index_start, index_offset, -+ sizeof(sindex), &index_start, -+ &index_offset); -+ SQUASHFS_SWAP_DIR_INDEX_2(index, &sindex); -+ } else -+ squashfs_get_cached_block(s, (char *) index, -+ index_start, index_offset, -+ sizeof(struct squashfs_dir_index_2), -+ &index_start, &index_offset); -+ -+ squashfs_get_cached_block(s, index->name, index_start, -+ index_offset, index->size + 1, -+ &index_start, &index_offset); -+ -+ index->name[index->size + 1] = '\0'; -+ -+ if (strcmp(index->name, str) > 0) -+ break; -+ -+ length = index->index; -+ *next_block = index->start_block + sblk->directory_table_start; -+ } -+ -+ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE; -+ return length; -+} -+ -+ -+static int squashfs_readdir_2(struct file *file, void *dirent, filldir_t filldir) -+{ -+ struct inode *i = file->f_dentry->d_inode; -+ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info; -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ long long next_block = SQUASHFS_I(i)->start_block + -+ sblk->directory_table_start; -+ int next_offset = SQUASHFS_I(i)->offset, length = 0, dirs_read = 0, -+ dir_count; -+ struct squashfs_dir_header_2 dirh; -+ char buffer[sizeof(struct squashfs_dir_entry_2) + SQUASHFS_NAME_LEN + 1]; -+ struct squashfs_dir_entry_2 *dire = (struct squashfs_dir_entry_2 *) buffer; -+ -+ TRACE("Entered squashfs_readdir_2 [%llx:%x]\n", next_block, next_offset); -+ -+ length = get_dir_index_using_offset(i->i_sb, &next_block, &next_offset, -+ SQUASHFS_I(i)->u.s2.directory_index_start, -+ SQUASHFS_I(i)->u.s2.directory_index_offset, -+ SQUASHFS_I(i)->u.s2.directory_index_count, -+ file->f_pos); -+ -+ while (length < i_size_read(i)) { -+ /* read directory header */ -+ if (msblk->swap) { -+ struct squashfs_dir_header_2 sdirh; -+ -+ if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh, -+ next_block, next_offset, sizeof(sdirh), -+ &next_block, &next_offset)) -+ goto failed_read; -+ -+ length += sizeof(sdirh); -+ SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh); -+ } else { -+ if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh, -+ next_block, next_offset, sizeof(dirh), -+ &next_block, &next_offset)) -+ goto failed_read; -+ -+ length += sizeof(dirh); -+ } -+ -+ dir_count = dirh.count + 1; -+ while (dir_count--) { -+ if (msblk->swap) { -+ struct squashfs_dir_entry_2 sdire; -+ if (!squashfs_get_cached_block(i->i_sb, (char *) -+ &sdire, next_block, next_offset, -+ sizeof(sdire), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ length += sizeof(sdire); -+ SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire); -+ } else { -+ if (!squashfs_get_cached_block(i->i_sb, (char *) -+ dire, next_block, next_offset, -+ sizeof(*dire), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ length += sizeof(*dire); -+ } -+ -+ if (!squashfs_get_cached_block(i->i_sb, dire->name, -+ next_block, next_offset, -+ dire->size + 1, &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ length += dire->size + 1; -+ -+ if (file->f_pos >= length) -+ continue; -+ -+ dire->name[dire->size + 1] = '\0'; -+ -+ TRACE("Calling filldir(%x, %s, %d, %d, %x:%x, %d)\n", -+ (unsigned int) dirent, dire->name, -+ dire->size + 1, (int) file->f_pos, -+ dirh.start_block, dire->offset, -+ squashfs_filetype_table[dire->type]); -+ -+ if (filldir(dirent, dire->name, dire->size + 1, -+ file->f_pos, SQUASHFS_MK_VFS_INODE( -+ dirh.start_block, dire->offset), -+ squashfs_filetype_table[dire->type]) -+ < 0) { -+ TRACE("Filldir returned less than 0\n"); -+ goto finish; -+ } -+ file->f_pos = length; -+ dirs_read++; -+ } -+ } -+ -+finish: -+ return dirs_read; -+ -+failed_read: -+ ERROR("Unable to read directory block [%llx:%x]\n", next_block, -+ next_offset); -+ return 0; -+} -+ -+ -+static struct dentry *squashfs_lookup_2(struct inode *i, struct dentry *dentry, -+ struct nameidata *nd) -+{ -+ const unsigned char *name = dentry->d_name.name; -+ int len = dentry->d_name.len; -+ struct inode *inode = NULL; -+ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info; -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ long long next_block = SQUASHFS_I(i)->start_block + -+ sblk->directory_table_start; -+ int next_offset = SQUASHFS_I(i)->offset, length = 0, -+ dir_count; -+ struct squashfs_dir_header_2 dirh; -+ char buffer[sizeof(struct squashfs_dir_entry_2) + SQUASHFS_NAME_LEN]; -+ struct squashfs_dir_entry_2 *dire = (struct squashfs_dir_entry_2 *) buffer; -+ int sorted = sblk->s_major == 2 && sblk->s_minor >= 1; -+ -+ TRACE("Entered squashfs_lookup [%llx:%x]\n", next_block, next_offset); -+ -+ if (len > SQUASHFS_NAME_LEN) -+ goto exit_loop; -+ -+ length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset, -+ SQUASHFS_I(i)->u.s2.directory_index_start, -+ SQUASHFS_I(i)->u.s2.directory_index_offset, -+ SQUASHFS_I(i)->u.s2.directory_index_count, name, -+ len); -+ -+ while (length < i_size_read(i)) { -+ /* read directory header */ -+ if (msblk->swap) { -+ struct squashfs_dir_header_2 sdirh; -+ if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh, -+ next_block, next_offset, sizeof(sdirh), -+ &next_block, &next_offset)) -+ goto failed_read; -+ -+ length += sizeof(sdirh); -+ SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh); -+ } else { -+ if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh, -+ next_block, next_offset, sizeof(dirh), -+ &next_block, &next_offset)) -+ goto failed_read; -+ -+ length += sizeof(dirh); -+ } -+ -+ dir_count = dirh.count + 1; -+ while (dir_count--) { -+ if (msblk->swap) { -+ struct squashfs_dir_entry_2 sdire; -+ if (!squashfs_get_cached_block(i->i_sb, (char *) -+ &sdire, next_block,next_offset, -+ sizeof(sdire), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ length += sizeof(sdire); -+ SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire); -+ } else { -+ if (!squashfs_get_cached_block(i->i_sb, (char *) -+ dire, next_block,next_offset, -+ sizeof(*dire), &next_block, -+ &next_offset)) -+ goto failed_read; -+ -+ length += sizeof(*dire); -+ } -+ -+ if (!squashfs_get_cached_block(i->i_sb, dire->name, -+ next_block, next_offset, dire->size + 1, -+ &next_block, &next_offset)) -+ goto failed_read; -+ -+ length += dire->size + 1; -+ -+ if (sorted && name[0] < dire->name[0]) -+ goto exit_loop; -+ -+ if ((len == dire->size + 1) && !strncmp(name, -+ dire->name, len)) { -+ squashfs_inode_t ino = -+ SQUASHFS_MKINODE(dirh.start_block, -+ dire->offset); -+ -+ TRACE("calling squashfs_iget for directory " -+ "entry %s, inode %x:%x, %lld\n", name, -+ dirh.start_block, dire->offset, ino); -+ -+ inode = (msblk->iget)(i->i_sb, ino); -+ -+ goto exit_loop; -+ } -+ } -+ } -+ -+exit_loop: -+ d_add(dentry, inode); -+ return ERR_PTR(0); -+ -+failed_read: -+ ERROR("Unable to read directory block [%llx:%x]\n", next_block, -+ next_offset); -+ goto exit_loop; -+} -+ -+ -+int squashfs_2_0_supported(struct squashfs_sb_info *msblk) -+{ -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ -+ msblk->iget = squashfs_iget_2; -+ msblk->read_fragment_index_table = read_fragment_index_table_2; -+ -+ sblk->bytes_used = sblk->bytes_used_2; -+ sblk->uid_start = sblk->uid_start_2; -+ sblk->guid_start = sblk->guid_start_2; -+ sblk->inode_table_start = sblk->inode_table_start_2; -+ sblk->directory_table_start = sblk->directory_table_start_2; -+ sblk->fragment_table_start = sblk->fragment_table_start_2; -+ -+ return 1; -+} -Index: linux-2.6.22/fs/squashfs/squashfs.h -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/fs/squashfs/squashfs.h 2007-08-28 21:56:34.000000000 +0100 -@@ -0,0 +1,86 @@ -+/* -+ * Squashfs - a compressed read only filesystem for Linux -+ * -+ * Copyright (c) 2002, 2003, 2004, 2005, 2006 -+ * Phillip Lougher <phillip@lougher.org.uk> -+ * -+ * 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 the Free Software Foundation; either version 2, -+ * or (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -+ * -+ * squashfs.h -+ */ -+ -+#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY -+#undef CONFIG_SQUASHFS_1_0_COMPATIBILITY -+#endif -+ -+#ifdef SQUASHFS_TRACE -+#define TRACE(s, args...) printk(KERN_NOTICE "SQUASHFS: "s, ## args) -+#else -+#define TRACE(s, args...) {} -+#endif -+ -+#define ERROR(s, args...) printk(KERN_ERR "SQUASHFS error: "s, ## args) -+ -+#define SERROR(s, args...) do { \ -+ if (!silent) \ -+ printk(KERN_ERR "SQUASHFS error: "s, ## args);\ -+ } while(0) -+ -+#define WARNING(s, args...) printk(KERN_WARNING "SQUASHFS: "s, ## args) -+ -+static inline struct squashfs_inode_info *SQUASHFS_I(struct inode *inode) -+{ -+ return list_entry(inode, struct squashfs_inode_info, vfs_inode); -+} -+ -+#if defined(CONFIG_SQUASHFS_1_0_COMPATIBILITY ) || defined(CONFIG_SQUASHFS_2_0_COMPATIBILITY) -+#define SQSH_EXTERN -+extern unsigned int squashfs_read_data(struct super_block *s, char *buffer, -+ long long index, unsigned int length, -+ long long *next_index); -+extern int squashfs_get_cached_block(struct super_block *s, char *buffer, -+ long long block, unsigned int offset, -+ int length, long long *next_block, -+ unsigned int *next_offset); -+extern void release_cached_fragment(struct squashfs_sb_info *msblk, struct -+ squashfs_fragment_cache *fragment); -+extern struct squashfs_fragment_cache *get_cached_fragment(struct super_block -+ *s, long long start_block, -+ int length); -+extern struct address_space_operations squashfs_symlink_aops; -+extern struct address_space_operations squashfs_aops; -+extern struct address_space_operations squashfs_aops_4K; -+extern struct inode_operations squashfs_dir_inode_ops; -+#else -+#define SQSH_EXTERN static -+#endif -+ -+#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY -+extern int squashfs_1_0_supported(struct squashfs_sb_info *msblk); -+#else -+static inline int squashfs_1_0_supported(struct squashfs_sb_info *msblk) -+{ -+ return 0; -+} -+#endif -+ -+#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY -+extern int squashfs_2_0_supported(struct squashfs_sb_info *msblk); -+#else -+static inline int squashfs_2_0_supported(struct squashfs_sb_info *msblk) -+{ -+ return 0; -+} -+#endif -Index: linux-2.6.22/include/linux/squashfs_fs.h -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/include/linux/squashfs_fs.h 2007-08-28 21:56:34.000000000 +0100 -@@ -0,0 +1,911 @@ -+#ifndef SQUASHFS_FS -+#define SQUASHFS_FS -+ -+/* -+ * Squashfs -+ * -+ * Copyright (c) 2002, 2003, 2004, 2005, 2006 -+ * Phillip Lougher <phillip@lougher.org.uk> -+ * -+ * 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 the Free Software Foundation; either version 2, -+ * or (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -+ * -+ * squashfs_fs.h -+ */ -+ -+#ifndef CONFIG_SQUASHFS_2_0_COMPATIBILITY -+#define CONFIG_SQUASHFS_2_0_COMPATIBILITY -+#endif -+ -+#ifdef CONFIG_SQUASHFS_VMALLOC -+#define SQUASHFS_ALLOC(a) vmalloc(a) -+#define SQUASHFS_FREE(a) vfree(a) -+#else -+#define SQUASHFS_ALLOC(a) kmalloc(a, GFP_KERNEL) -+#define SQUASHFS_FREE(a) kfree(a) -+#endif -+#define SQUASHFS_CACHED_FRAGMENTS CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE -+#define SQUASHFS_MAJOR 3 -+#define SQUASHFS_MINOR 0 -+#define SQUASHFS_MAGIC 0x73717368 -+#define SQUASHFS_MAGIC_SWAP 0x68737173 -+#define SQUASHFS_START 0 -+ -+/* size of metadata (inode and directory) blocks */ -+#define SQUASHFS_METADATA_SIZE 8192 -+#define SQUASHFS_METADATA_LOG 13 -+ -+/* default size of data blocks */ -+#define SQUASHFS_FILE_SIZE 65536 -+#define SQUASHFS_FILE_LOG 16 -+ -+#define SQUASHFS_FILE_MAX_SIZE 65536 -+ -+/* Max number of uids and gids */ -+#define SQUASHFS_UIDS 256 -+#define SQUASHFS_GUIDS 255 -+ -+/* Max length of filename (not 255) */ -+#define SQUASHFS_NAME_LEN 256 -+ -+#define SQUASHFS_INVALID ((long long) 0xffffffffffff) -+#define SQUASHFS_INVALID_FRAG ((unsigned int) 0xffffffff) -+#define SQUASHFS_INVALID_BLK ((long long) -1) -+#define SQUASHFS_USED_BLK ((long long) -2) -+ -+/* Filesystem flags */ -+#define SQUASHFS_NOI 0 -+#define SQUASHFS_NOD 1 -+#define SQUASHFS_CHECK 2 -+#define SQUASHFS_NOF 3 -+#define SQUASHFS_NO_FRAG 4 -+#define SQUASHFS_ALWAYS_FRAG 5 -+#define SQUASHFS_DUPLICATE 6 -+ -+#define SQUASHFS_BIT(flag, bit) ((flag >> bit) & 1) -+ -+#define SQUASHFS_UNCOMPRESSED_INODES(flags) SQUASHFS_BIT(flags, \ -+ SQUASHFS_NOI) -+ -+#define SQUASHFS_UNCOMPRESSED_DATA(flags) SQUASHFS_BIT(flags, \ -+ SQUASHFS_NOD) -+ -+#define SQUASHFS_UNCOMPRESSED_FRAGMENTS(flags) SQUASHFS_BIT(flags, \ -+ SQUASHFS_NOF) -+ -+#define SQUASHFS_NO_FRAGMENTS(flags) SQUASHFS_BIT(flags, \ -+ SQUASHFS_NO_FRAG) -+ -+#define SQUASHFS_ALWAYS_FRAGMENTS(flags) SQUASHFS_BIT(flags, \ -+ SQUASHFS_ALWAYS_FRAG) -+ -+#define SQUASHFS_DUPLICATES(flags) SQUASHFS_BIT(flags, \ -+ SQUASHFS_DUPLICATE) -+ -+#define SQUASHFS_CHECK_DATA(flags) SQUASHFS_BIT(flags, \ -+ SQUASHFS_CHECK) -+ -+#define SQUASHFS_MKFLAGS(noi, nod, check_data, nof, no_frag, always_frag, \ -+ duplicate_checking) (noi | (nod << 1) | (check_data << 2) \ -+ | (nof << 3) | (no_frag << 4) | (always_frag << 5) | \ -+ (duplicate_checking << 6)) -+ -+/* Max number of types and file types */ -+#define SQUASHFS_DIR_TYPE 1 -+#define SQUASHFS_FILE_TYPE 2 -+#define SQUASHFS_SYMLINK_TYPE 3 -+#define SQUASHFS_BLKDEV_TYPE 4 -+#define SQUASHFS_CHRDEV_TYPE 5 -+#define SQUASHFS_FIFO_TYPE 6 -+#define SQUASHFS_SOCKET_TYPE 7 -+#define SQUASHFS_LDIR_TYPE 8 -+#define SQUASHFS_LREG_TYPE 9 -+ -+/* 1.0 filesystem type definitions */ -+#define SQUASHFS_TYPES 5 -+#define SQUASHFS_IPC_TYPE 0 -+ -+/* Flag whether block is compressed or uncompressed, bit is set if block is -+ * uncompressed */ -+#define SQUASHFS_COMPRESSED_BIT (1 << 15) -+ -+#define SQUASHFS_COMPRESSED_SIZE(B) (((B) & ~SQUASHFS_COMPRESSED_BIT) ? \ -+ (B) & ~SQUASHFS_COMPRESSED_BIT : SQUASHFS_COMPRESSED_BIT) -+ -+#define SQUASHFS_COMPRESSED(B) (!((B) & SQUASHFS_COMPRESSED_BIT)) -+ -+#define SQUASHFS_COMPRESSED_BIT_BLOCK (1 << 24) -+ -+#define SQUASHFS_COMPRESSED_SIZE_BLOCK(B) (((B) & \ -+ ~SQUASHFS_COMPRESSED_BIT_BLOCK) ? (B) & \ -+ ~SQUASHFS_COMPRESSED_BIT_BLOCK : SQUASHFS_COMPRESSED_BIT_BLOCK) -+ -+#define SQUASHFS_COMPRESSED_BLOCK(B) (!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK)) -+ -+/* -+ * Inode number ops. Inodes consist of a compressed block number, and an -+ * uncompressed offset within that block -+ */ -+#define SQUASHFS_INODE_BLK(a) ((unsigned int) ((a) >> 16)) -+ -+#define SQUASHFS_INODE_OFFSET(a) ((unsigned int) ((a) & 0xffff)) -+ -+#define SQUASHFS_MKINODE(A, B) ((squashfs_inode_t)(((squashfs_inode_t) (A)\ -+ << 16) + (B))) -+ -+/* Compute 32 bit VFS inode number from squashfs inode number */ -+#define SQUASHFS_MK_VFS_INODE(a, b) ((unsigned int) (((a) << 8) + \ -+ ((b) >> 2) + 1)) -+/* XXX */ -+ -+/* Translate between VFS mode and squashfs mode */ -+#define SQUASHFS_MODE(a) ((a) & 0xfff) -+ -+/* fragment and fragment table defines */ -+#define SQUASHFS_FRAGMENT_BYTES(A) (A * sizeof(struct squashfs_fragment_entry)) -+ -+#define SQUASHFS_FRAGMENT_INDEX(A) (SQUASHFS_FRAGMENT_BYTES(A) / \ -+ SQUASHFS_METADATA_SIZE) -+ -+#define SQUASHFS_FRAGMENT_INDEX_OFFSET(A) (SQUASHFS_FRAGMENT_BYTES(A) % \ -+ SQUASHFS_METADATA_SIZE) -+ -+#define SQUASHFS_FRAGMENT_INDEXES(A) ((SQUASHFS_FRAGMENT_BYTES(A) + \ -+ SQUASHFS_METADATA_SIZE - 1) / \ -+ SQUASHFS_METADATA_SIZE) -+ -+#define SQUASHFS_FRAGMENT_INDEX_BYTES(A) (SQUASHFS_FRAGMENT_INDEXES(A) *\ -+ sizeof(long long)) -+ -+/* cached data constants for filesystem */ -+#define SQUASHFS_CACHED_BLKS 8 -+ -+#define SQUASHFS_MAX_FILE_SIZE_LOG 64 -+ -+#define SQUASHFS_MAX_FILE_SIZE ((long long) 1 << \ -+ (SQUASHFS_MAX_FILE_SIZE_LOG - 2)) -+ -+#define SQUASHFS_MARKER_BYTE 0xff -+ -+/* meta index cache */ -+#define SQUASHFS_META_INDEXES (SQUASHFS_METADATA_SIZE / sizeof(unsigned int)) -+#define SQUASHFS_META_ENTRIES 31 -+#define SQUASHFS_META_NUMBER 8 -+#define SQUASHFS_SLOTS 4 -+ -+struct meta_entry { -+ long long data_block; -+ unsigned int index_block; -+ unsigned short offset; -+ unsigned short pad; -+}; -+ -+struct meta_index { -+ unsigned int inode_number; -+ unsigned int offset; -+ unsigned short entries; -+ unsigned short skip; -+ unsigned short locked; -+ unsigned short pad; -+ struct meta_entry meta_entry[SQUASHFS_META_ENTRIES]; -+}; -+ -+ -+/* -+ * definitions for structures on disk -+ */ -+ -+typedef long long squashfs_block_t; -+typedef long long squashfs_inode_t; -+ -+struct squashfs_super_block { -+ unsigned int s_magic; -+ unsigned int inodes; -+ unsigned int bytes_used_2; -+ unsigned int uid_start_2; -+ unsigned int guid_start_2; -+ unsigned int inode_table_start_2; -+ unsigned int directory_table_start_2; -+ unsigned int s_major:16; -+ unsigned int s_minor:16; -+ unsigned int block_size_1:16; -+ unsigned int block_log:16; -+ unsigned int flags:8; -+ unsigned int no_uids:8; -+ unsigned int no_guids:8; -+ unsigned int mkfs_time /* time of filesystem creation */; -+ squashfs_inode_t root_inode; -+ unsigned int block_size; -+ unsigned int fragments; -+ unsigned int fragment_table_start_2; -+ long long bytes_used; -+ long long uid_start; -+ long long guid_start; -+ long long inode_table_start; -+ long long directory_table_start; -+ long long fragment_table_start; -+ long long unused; -+} __attribute__ ((packed)); -+ -+struct squashfs_dir_index { -+ unsigned int index; -+ unsigned int start_block; -+ unsigned char size; -+ unsigned char name[0]; -+} __attribute__ ((packed)); -+ -+#define SQUASHFS_BASE_INODE_HEADER \ -+ unsigned int inode_type:4; \ -+ unsigned int mode:12; \ -+ unsigned int uid:8; \ -+ unsigned int guid:8; \ -+ unsigned int mtime; \ -+ unsigned int inode_number; -+ -+struct squashfs_base_inode_header { -+ SQUASHFS_BASE_INODE_HEADER; -+} __attribute__ ((packed)); -+ -+struct squashfs_ipc_inode_header { -+ SQUASHFS_BASE_INODE_HEADER; -+ unsigned int nlink; -+} __attribute__ ((packed)); -+ -+struct squashfs_dev_inode_header { -+ SQUASHFS_BASE_INODE_HEADER; -+ unsigned int nlink; -+ unsigned short rdev; -+} __attribute__ ((packed)); -+ -+struct squashfs_symlink_inode_header { -+ SQUASHFS_BASE_INODE_HEADER; -+ unsigned int nlink; -+ unsigned short symlink_size; -+ char symlink[0]; -+} __attribute__ ((packed)); -+ -+struct squashfs_reg_inode_header { -+ SQUASHFS_BASE_INODE_HEADER; -+ squashfs_block_t start_block; -+ unsigned int fragment; -+ unsigned int offset; -+ unsigned int file_size; -+ unsigned short block_list[0]; -+} __attribute__ ((packed)); -+ -+struct squashfs_lreg_inode_header { -+ SQUASHFS_BASE_INODE_HEADER; -+ unsigned int nlink; -+ squashfs_block_t start_block; -+ unsigned int fragment; -+ unsigned int offset; -+ long long file_size; -+ unsigned short block_list[0]; -+} __attribute__ ((packed)); -+ -+struct squashfs_dir_inode_header { -+ SQUASHFS_BASE_INODE_HEADER; -+ unsigned int nlink; -+ unsigned int file_size:19; -+ unsigned int offset:13; -+ unsigned int start_block; -+ unsigned int parent_inode; -+} __attribute__ ((packed)); -+ -+struct squashfs_ldir_inode_header { -+ SQUASHFS_BASE_INODE_HEADER; -+ unsigned int nlink; -+ unsigned int file_size:27; -+ unsigned int offset:13; -+ unsigned int start_block; -+ unsigned int i_count:16; -+ unsigned int parent_inode; -+ struct squashfs_dir_index index[0]; -+} __attribute__ ((packed)); -+ -+union squashfs_inode_header { -+ struct squashfs_base_inode_header base; -+ struct squashfs_dev_inode_header dev; -+ struct squashfs_symlink_inode_header symlink; -+ struct squashfs_reg_inode_header reg; -+ struct squashfs_lreg_inode_header lreg; -+ struct squashfs_dir_inode_header dir; -+ struct squashfs_ldir_inode_header ldir; -+ struct squashfs_ipc_inode_header ipc; -+}; -+ -+struct squashfs_dir_entry { -+ unsigned int offset:13; -+ unsigned int type:3; -+ unsigned int size:8; -+ int inode_number:16; -+ char name[0]; -+} __attribute__ ((packed)); -+ -+struct squashfs_dir_header { -+ unsigned int count:8; -+ unsigned int start_block; -+ unsigned int inode_number; -+} __attribute__ ((packed)); -+ -+struct squashfs_fragment_entry { -+ long long start_block; -+ unsigned int size; -+ unsigned int unused; -+} __attribute__ ((packed)); -+ -+extern int squashfs_uncompress_block(void *d, int dstlen, void *s, int srclen); -+extern int squashfs_uncompress_init(void); -+extern int squashfs_uncompress_exit(void); -+ -+/* -+ * macros to convert each packed bitfield structure from little endian to big -+ * endian and vice versa. These are needed when creating or using a filesystem -+ * on a machine with different byte ordering to the target architecture. -+ * -+ */ -+ -+#define SQUASHFS_SWAP_START \ -+ int bits;\ -+ int b_pos;\ -+ unsigned long long val;\ -+ unsigned char *s;\ -+ unsigned char *d; -+ -+#define SQUASHFS_SWAP_SUPER_BLOCK(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_super_block));\ -+ SQUASHFS_SWAP((s)->s_magic, d, 0, 32);\ -+ SQUASHFS_SWAP((s)->inodes, d, 32, 32);\ -+ SQUASHFS_SWAP((s)->bytes_used_2, d, 64, 32);\ -+ SQUASHFS_SWAP((s)->uid_start_2, d, 96, 32);\ -+ SQUASHFS_SWAP((s)->guid_start_2, d, 128, 32);\ -+ SQUASHFS_SWAP((s)->inode_table_start_2, d, 160, 32);\ -+ SQUASHFS_SWAP((s)->directory_table_start_2, d, 192, 32);\ -+ SQUASHFS_SWAP((s)->s_major, d, 224, 16);\ -+ SQUASHFS_SWAP((s)->s_minor, d, 240, 16);\ -+ SQUASHFS_SWAP((s)->block_size_1, d, 256, 16);\ -+ SQUASHFS_SWAP((s)->block_log, d, 272, 16);\ -+ SQUASHFS_SWAP((s)->flags, d, 288, 8);\ -+ SQUASHFS_SWAP((s)->no_uids, d, 296, 8);\ -+ SQUASHFS_SWAP((s)->no_guids, d, 304, 8);\ -+ SQUASHFS_SWAP((s)->mkfs_time, d, 312, 32);\ -+ SQUASHFS_SWAP((s)->root_inode, d, 344, 64);\ -+ SQUASHFS_SWAP((s)->block_size, d, 408, 32);\ -+ SQUASHFS_SWAP((s)->fragments, d, 440, 32);\ -+ SQUASHFS_SWAP((s)->fragment_table_start_2, d, 472, 32);\ -+ SQUASHFS_SWAP((s)->bytes_used, d, 504, 64);\ -+ SQUASHFS_SWAP((s)->uid_start, d, 568, 64);\ -+ SQUASHFS_SWAP((s)->guid_start, d, 632, 64);\ -+ SQUASHFS_SWAP((s)->inode_table_start, d, 696, 64);\ -+ SQUASHFS_SWAP((s)->directory_table_start, d, 760, 64);\ -+ SQUASHFS_SWAP((s)->fragment_table_start, d, 824, 64);\ -+ SQUASHFS_SWAP((s)->unused, d, 888, 64);\ -+} -+ -+#define SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\ -+ SQUASHFS_MEMSET(s, d, n);\ -+ SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\ -+ SQUASHFS_SWAP((s)->mode, d, 4, 12);\ -+ SQUASHFS_SWAP((s)->uid, d, 16, 8);\ -+ SQUASHFS_SWAP((s)->guid, d, 24, 8);\ -+ SQUASHFS_SWAP((s)->mtime, d, 32, 32);\ -+ SQUASHFS_SWAP((s)->inode_number, d, 64, 32); -+ -+#define SQUASHFS_SWAP_BASE_INODE_HEADER(s, d, n) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\ -+} -+ -+#define SQUASHFS_SWAP_IPC_INODE_HEADER(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ -+ sizeof(struct squashfs_ipc_inode_header))\ -+ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ -+} -+ -+#define SQUASHFS_SWAP_DEV_INODE_HEADER(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ -+ sizeof(struct squashfs_dev_inode_header)); \ -+ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ -+ SQUASHFS_SWAP((s)->rdev, d, 128, 16);\ -+} -+ -+#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ -+ sizeof(struct squashfs_symlink_inode_header));\ -+ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ -+ SQUASHFS_SWAP((s)->symlink_size, d, 128, 16);\ -+} -+ -+#define SQUASHFS_SWAP_REG_INODE_HEADER(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ -+ sizeof(struct squashfs_reg_inode_header));\ -+ SQUASHFS_SWAP((s)->start_block, d, 96, 64);\ -+ SQUASHFS_SWAP((s)->fragment, d, 160, 32);\ -+ SQUASHFS_SWAP((s)->offset, d, 192, 32);\ -+ SQUASHFS_SWAP((s)->file_size, d, 224, 32);\ -+} -+ -+#define SQUASHFS_SWAP_LREG_INODE_HEADER(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ -+ sizeof(struct squashfs_lreg_inode_header));\ -+ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ -+ SQUASHFS_SWAP((s)->start_block, d, 128, 64);\ -+ SQUASHFS_SWAP((s)->fragment, d, 192, 32);\ -+ SQUASHFS_SWAP((s)->offset, d, 224, 32);\ -+ SQUASHFS_SWAP((s)->file_size, d, 256, 64);\ -+} -+ -+#define SQUASHFS_SWAP_DIR_INODE_HEADER(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ -+ sizeof(struct squashfs_dir_inode_header));\ -+ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ -+ SQUASHFS_SWAP((s)->file_size, d, 128, 19);\ -+ SQUASHFS_SWAP((s)->offset, d, 147, 13);\ -+ SQUASHFS_SWAP((s)->start_block, d, 160, 32);\ -+ SQUASHFS_SWAP((s)->parent_inode, d, 192, 32);\ -+} -+ -+#define SQUASHFS_SWAP_LDIR_INODE_HEADER(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ -+ sizeof(struct squashfs_ldir_inode_header));\ -+ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ -+ SQUASHFS_SWAP((s)->file_size, d, 128, 27);\ -+ SQUASHFS_SWAP((s)->offset, d, 155, 13);\ -+ SQUASHFS_SWAP((s)->start_block, d, 168, 32);\ -+ SQUASHFS_SWAP((s)->i_count, d, 200, 16);\ -+ SQUASHFS_SWAP((s)->parent_inode, d, 216, 32);\ -+} -+ -+#define SQUASHFS_SWAP_DIR_INDEX(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index));\ -+ SQUASHFS_SWAP((s)->index, d, 0, 32);\ -+ SQUASHFS_SWAP((s)->start_block, d, 32, 32);\ -+ SQUASHFS_SWAP((s)->size, d, 64, 8);\ -+} -+ -+#define SQUASHFS_SWAP_DIR_HEADER(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header));\ -+ SQUASHFS_SWAP((s)->count, d, 0, 8);\ -+ SQUASHFS_SWAP((s)->start_block, d, 8, 32);\ -+ SQUASHFS_SWAP((s)->inode_number, d, 40, 32);\ -+} -+ -+#define SQUASHFS_SWAP_DIR_ENTRY(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry));\ -+ SQUASHFS_SWAP((s)->offset, d, 0, 13);\ -+ SQUASHFS_SWAP((s)->type, d, 13, 3);\ -+ SQUASHFS_SWAP((s)->size, d, 16, 8);\ -+ SQUASHFS_SWAP((s)->inode_number, d, 24, 16);\ -+} -+ -+#define SQUASHFS_SWAP_FRAGMENT_ENTRY(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry));\ -+ SQUASHFS_SWAP((s)->start_block, d, 0, 64);\ -+ SQUASHFS_SWAP((s)->size, d, 64, 32);\ -+} -+ -+#define SQUASHFS_SWAP_SHORTS(s, d, n) {\ -+ int entry;\ -+ int bit_position;\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_MEMSET(s, d, n * 2);\ -+ for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \ -+ 16)\ -+ SQUASHFS_SWAP(s[entry], d, bit_position, 16);\ -+} -+ -+#define SQUASHFS_SWAP_INTS(s, d, n) {\ -+ int entry;\ -+ int bit_position;\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_MEMSET(s, d, n * 4);\ -+ for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \ -+ 32)\ -+ SQUASHFS_SWAP(s[entry], d, bit_position, 32);\ -+} -+ -+#define SQUASHFS_SWAP_LONG_LONGS(s, d, n) {\ -+ int entry;\ -+ int bit_position;\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_MEMSET(s, d, n * 8);\ -+ for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \ -+ 64)\ -+ SQUASHFS_SWAP(s[entry], d, bit_position, 64);\ -+} -+ -+#define SQUASHFS_SWAP_DATA(s, d, n, bits) {\ -+ int entry;\ -+ int bit_position;\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_MEMSET(s, d, n * bits / 8);\ -+ for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \ -+ bits)\ -+ SQUASHFS_SWAP(s[entry], d, bit_position, bits);\ -+} -+ -+#define SQUASHFS_SWAP_FRAGMENT_INDEXES(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n) -+ -+#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY -+ -+struct squashfs_base_inode_header_1 { -+ unsigned int inode_type:4; -+ unsigned int mode:12; /* protection */ -+ unsigned int uid:4; /* index into uid table */ -+ unsigned int guid:4; /* index into guid table */ -+} __attribute__ ((packed)); -+ -+struct squashfs_ipc_inode_header_1 { -+ unsigned int inode_type:4; -+ unsigned int mode:12; /* protection */ -+ unsigned int uid:4; /* index into uid table */ -+ unsigned int guid:4; /* index into guid table */ -+ unsigned int type:4; -+ unsigned int offset:4; -+} __attribute__ ((packed)); -+ -+struct squashfs_dev_inode_header_1 { -+ unsigned int inode_type:4; -+ unsigned int mode:12; /* protection */ -+ unsigned int uid:4; /* index into uid table */ -+ unsigned int guid:4; /* index into guid table */ -+ unsigned short rdev; -+} __attribute__ ((packed)); -+ -+struct squashfs_symlink_inode_header_1 { -+ unsigned int inode_type:4; -+ unsigned int mode:12; /* protection */ -+ unsigned int uid:4; /* index into uid table */ -+ unsigned int guid:4; /* index into guid table */ -+ unsigned short symlink_size; -+ char symlink[0]; -+} __attribute__ ((packed)); -+ -+struct squashfs_reg_inode_header_1 { -+ unsigned int inode_type:4; -+ unsigned int mode:12; /* protection */ -+ unsigned int uid:4; /* index into uid table */ -+ unsigned int guid:4; /* index into guid table */ -+ unsigned int mtime; -+ unsigned int start_block; -+ unsigned int file_size:32; -+ unsigned short block_list[0]; -+} __attribute__ ((packed)); -+ -+struct squashfs_dir_inode_header_1 { -+ unsigned int inode_type:4; -+ unsigned int mode:12; /* protection */ -+ unsigned int uid:4; /* index into uid table */ -+ unsigned int guid:4; /* index into guid table */ -+ unsigned int file_size:19; -+ unsigned int offset:13; -+ unsigned int mtime; -+ unsigned int start_block:24; -+} __attribute__ ((packed)); -+ -+#define SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n) \ -+ SQUASHFS_MEMSET(s, d, n);\ -+ SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\ -+ SQUASHFS_SWAP((s)->mode, d, 4, 12);\ -+ SQUASHFS_SWAP((s)->uid, d, 16, 4);\ -+ SQUASHFS_SWAP((s)->guid, d, 20, 4); -+ -+#define SQUASHFS_SWAP_BASE_INODE_HEADER_1(s, d, n) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n)\ -+} -+ -+#define SQUASHFS_SWAP_IPC_INODE_HEADER_1(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ -+ sizeof(struct squashfs_ipc_inode_header_1));\ -+ SQUASHFS_SWAP((s)->type, d, 24, 4);\ -+ SQUASHFS_SWAP((s)->offset, d, 28, 4);\ -+} -+ -+#define SQUASHFS_SWAP_DEV_INODE_HEADER_1(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ -+ sizeof(struct squashfs_dev_inode_header_1));\ -+ SQUASHFS_SWAP((s)->rdev, d, 24, 16);\ -+} -+ -+#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_1(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ -+ sizeof(struct squashfs_symlink_inode_header_1));\ -+ SQUASHFS_SWAP((s)->symlink_size, d, 24, 16);\ -+} -+ -+#define SQUASHFS_SWAP_REG_INODE_HEADER_1(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ -+ sizeof(struct squashfs_reg_inode_header_1));\ -+ SQUASHFS_SWAP((s)->mtime, d, 24, 32);\ -+ SQUASHFS_SWAP((s)->start_block, d, 56, 32);\ -+ SQUASHFS_SWAP((s)->file_size, d, 88, 32);\ -+} -+ -+#define SQUASHFS_SWAP_DIR_INODE_HEADER_1(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ -+ sizeof(struct squashfs_dir_inode_header_1));\ -+ SQUASHFS_SWAP((s)->file_size, d, 24, 19);\ -+ SQUASHFS_SWAP((s)->offset, d, 43, 13);\ -+ SQUASHFS_SWAP((s)->mtime, d, 56, 32);\ -+ SQUASHFS_SWAP((s)->start_block, d, 88, 24);\ -+} -+ -+#endif -+ -+#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY -+ -+struct squashfs_dir_index_2 { -+ unsigned int index:27; -+ unsigned int start_block:29; -+ unsigned char size; -+ unsigned char name[0]; -+} __attribute__ ((packed)); -+ -+struct squashfs_base_inode_header_2 { -+ unsigned int inode_type:4; -+ unsigned int mode:12; /* protection */ -+ unsigned int uid:8; /* index into uid table */ -+ unsigned int guid:8; /* index into guid table */ -+} __attribute__ ((packed)); -+ -+struct squashfs_ipc_inode_header_2 { -+ unsigned int inode_type:4; -+ unsigned int mode:12; /* protection */ -+ unsigned int uid:8; /* index into uid table */ -+ unsigned int guid:8; /* index into guid table */ -+} __attribute__ ((packed)); -+ -+struct squashfs_dev_inode_header_2 { -+ unsigned int inode_type:4; -+ unsigned int mode:12; /* protection */ -+ unsigned int uid:8; /* index into uid table */ -+ unsigned int guid:8; /* index into guid table */ -+ unsigned short rdev; -+} __attribute__ ((packed)); -+ -+struct squashfs_symlink_inode_header_2 { -+ unsigned int inode_type:4; -+ unsigned int mode:12; /* protection */ -+ unsigned int uid:8; /* index into uid table */ -+ unsigned int guid:8; /* index into guid table */ -+ unsigned short symlink_size; -+ char symlink[0]; -+} __attribute__ ((packed)); -+ -+struct squashfs_reg_inode_header_2 { -+ unsigned int inode_type:4; -+ unsigned int mode:12; /* protection */ -+ unsigned int uid:8; /* index into uid table */ -+ unsigned int guid:8; /* index into guid table */ -+ unsigned int mtime; -+ unsigned int start_block; -+ unsigned int fragment; -+ unsigned int offset; -+ unsigned int file_size:32; -+ unsigned short block_list[0]; -+} __attribute__ ((packed)); -+ -+struct squashfs_dir_inode_header_2 { -+ unsigned int inode_type:4; -+ unsigned int mode:12; /* protection */ -+ unsigned int uid:8; /* index into uid table */ -+ unsigned int guid:8; /* index into guid table */ -+ unsigned int file_size:19; -+ unsigned int offset:13; -+ unsigned int mtime; -+ unsigned int start_block:24; -+} __attribute__ ((packed)); -+ -+struct squashfs_ldir_inode_header_2 { -+ unsigned int inode_type:4; -+ unsigned int mode:12; /* protection */ -+ unsigned int uid:8; /* index into uid table */ -+ unsigned int guid:8; /* index into guid table */ -+ unsigned int file_size:27; -+ unsigned int offset:13; -+ unsigned int mtime; -+ unsigned int start_block:24; -+ unsigned int i_count:16; -+ struct squashfs_dir_index_2 index[0]; -+} __attribute__ ((packed)); -+ -+union squashfs_inode_header_2 { -+ struct squashfs_base_inode_header_2 base; -+ struct squashfs_dev_inode_header_2 dev; -+ struct squashfs_symlink_inode_header_2 symlink; -+ struct squashfs_reg_inode_header_2 reg; -+ struct squashfs_dir_inode_header_2 dir; -+ struct squashfs_ldir_inode_header_2 ldir; -+ struct squashfs_ipc_inode_header_2 ipc; -+}; -+ -+struct squashfs_dir_header_2 { -+ unsigned int count:8; -+ unsigned int start_block:24; -+} __attribute__ ((packed)); -+ -+struct squashfs_dir_entry_2 { -+ unsigned int offset:13; -+ unsigned int type:3; -+ unsigned int size:8; -+ char name[0]; -+} __attribute__ ((packed)); -+ -+struct squashfs_fragment_entry_2 { -+ unsigned int start_block; -+ unsigned int size; -+} __attribute__ ((packed)); -+ -+#define SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\ -+ SQUASHFS_MEMSET(s, d, n);\ -+ SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\ -+ SQUASHFS_SWAP((s)->mode, d, 4, 12);\ -+ SQUASHFS_SWAP((s)->uid, d, 16, 8);\ -+ SQUASHFS_SWAP((s)->guid, d, 24, 8);\ -+ -+#define SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, n) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\ -+} -+ -+#define SQUASHFS_SWAP_IPC_INODE_HEADER_2(s, d) \ -+ SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, sizeof(struct squashfs_ipc_inode_header_2)) -+ -+#define SQUASHFS_SWAP_DEV_INODE_HEADER_2(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ -+ sizeof(struct squashfs_dev_inode_header_2)); \ -+ SQUASHFS_SWAP((s)->rdev, d, 32, 16);\ -+} -+ -+#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ -+ sizeof(struct squashfs_symlink_inode_header_2));\ -+ SQUASHFS_SWAP((s)->symlink_size, d, 32, 16);\ -+} -+ -+#define SQUASHFS_SWAP_REG_INODE_HEADER_2(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ -+ sizeof(struct squashfs_reg_inode_header_2));\ -+ SQUASHFS_SWAP((s)->mtime, d, 32, 32);\ -+ SQUASHFS_SWAP((s)->start_block, d, 64, 32);\ -+ SQUASHFS_SWAP((s)->fragment, d, 96, 32);\ -+ SQUASHFS_SWAP((s)->offset, d, 128, 32);\ -+ SQUASHFS_SWAP((s)->file_size, d, 160, 32);\ -+} -+ -+#define SQUASHFS_SWAP_DIR_INODE_HEADER_2(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ -+ sizeof(struct squashfs_dir_inode_header_2));\ -+ SQUASHFS_SWAP((s)->file_size, d, 32, 19);\ -+ SQUASHFS_SWAP((s)->offset, d, 51, 13);\ -+ SQUASHFS_SWAP((s)->mtime, d, 64, 32);\ -+ SQUASHFS_SWAP((s)->start_block, d, 96, 24);\ -+} -+ -+#define SQUASHFS_SWAP_LDIR_INODE_HEADER_2(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ -+ sizeof(struct squashfs_ldir_inode_header_2));\ -+ SQUASHFS_SWAP((s)->file_size, d, 32, 27);\ -+ SQUASHFS_SWAP((s)->offset, d, 59, 13);\ -+ SQUASHFS_SWAP((s)->mtime, d, 72, 32);\ -+ SQUASHFS_SWAP((s)->start_block, d, 104, 24);\ -+ SQUASHFS_SWAP((s)->i_count, d, 128, 16);\ -+} -+ -+#define SQUASHFS_SWAP_DIR_INDEX_2(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index_2));\ -+ SQUASHFS_SWAP((s)->index, d, 0, 27);\ -+ SQUASHFS_SWAP((s)->start_block, d, 27, 29);\ -+ SQUASHFS_SWAP((s)->size, d, 56, 8);\ -+} -+#define SQUASHFS_SWAP_DIR_HEADER_2(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header_2));\ -+ SQUASHFS_SWAP((s)->count, d, 0, 8);\ -+ SQUASHFS_SWAP((s)->start_block, d, 8, 24);\ -+} -+ -+#define SQUASHFS_SWAP_DIR_ENTRY_2(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry_2));\ -+ SQUASHFS_SWAP((s)->offset, d, 0, 13);\ -+ SQUASHFS_SWAP((s)->type, d, 13, 3);\ -+ SQUASHFS_SWAP((s)->size, d, 16, 8);\ -+} -+ -+#define SQUASHFS_SWAP_FRAGMENT_ENTRY_2(s, d) {\ -+ SQUASHFS_SWAP_START\ -+ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry_2));\ -+ SQUASHFS_SWAP((s)->start_block, d, 0, 32);\ -+ SQUASHFS_SWAP((s)->size, d, 32, 32);\ -+} -+ -+#define SQUASHFS_SWAP_FRAGMENT_INDEXES_2(s, d, n) SQUASHFS_SWAP_INTS(s, d, n) -+ -+/* fragment and fragment table defines */ -+#define SQUASHFS_FRAGMENT_BYTES_2(A) (A * sizeof(struct squashfs_fragment_entry_2)) -+ -+#define SQUASHFS_FRAGMENT_INDEX_2(A) (SQUASHFS_FRAGMENT_BYTES_2(A) / \ -+ SQUASHFS_METADATA_SIZE) -+ -+#define SQUASHFS_FRAGMENT_INDEX_OFFSET_2(A) (SQUASHFS_FRAGMENT_BYTES_2(A) % \ -+ SQUASHFS_METADATA_SIZE) -+ -+#define SQUASHFS_FRAGMENT_INDEXES_2(A) ((SQUASHFS_FRAGMENT_BYTES_2(A) + \ -+ SQUASHFS_METADATA_SIZE - 1) / \ -+ SQUASHFS_METADATA_SIZE) -+ -+#define SQUASHFS_FRAGMENT_INDEX_BYTES_2(A) (SQUASHFS_FRAGMENT_INDEXES_2(A) *\ -+ sizeof(int)) -+ -+#endif -+ -+#ifdef __KERNEL__ -+ -+/* -+ * macros used to swap each structure entry, taking into account -+ * bitfields and different bitfield placing conventions on differing -+ * architectures -+ */ -+ -+#include <asm/byteorder.h> -+ -+#ifdef __BIG_ENDIAN -+ /* convert from little endian to big endian */ -+#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \ -+ tbits, b_pos) -+#else -+ /* convert from big endian to little endian */ -+#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \ -+ tbits, 64 - tbits - b_pos) -+#endif -+ -+#define _SQUASHFS_SWAP(value, p, pos, tbits, SHIFT) {\ -+ b_pos = pos % 8;\ -+ val = 0;\ -+ s = (unsigned char *)p + (pos / 8);\ -+ d = ((unsigned char *) &val) + 7;\ -+ for(bits = 0; bits < (tbits + b_pos); bits += 8) \ -+ *d-- = *s++;\ -+ value = (val >> (SHIFT))/* & ((1 << tbits) - 1)*/;\ -+} -+ -+#define SQUASHFS_MEMSET(s, d, n) memset(s, 0, n); -+ -+#endif -+#endif -Index: linux-2.6.22/include/linux/squashfs_fs_i.h -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/include/linux/squashfs_fs_i.h 2007-08-28 21:56:34.000000000 +0100 -@@ -0,0 +1,45 @@ -+#ifndef SQUASHFS_FS_I -+#define SQUASHFS_FS_I -+/* -+ * Squashfs -+ * -+ * Copyright (c) 2002, 2003, 2004, 2005, 2006 -+ * Phillip Lougher <phillip@lougher.org.uk> -+ * -+ * 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 the Free Software Foundation; either version 2, -+ * or (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -+ * -+ * squashfs_fs_i.h -+ */ -+ -+struct squashfs_inode_info { -+ long long start_block; -+ unsigned int offset; -+ union { -+ struct { -+ long long fragment_start_block; -+ unsigned int fragment_size; -+ unsigned int fragment_offset; -+ long long block_list_start; -+ } s1; -+ struct { -+ long long directory_index_start; -+ unsigned int directory_index_offset; -+ unsigned int directory_index_count; -+ unsigned int parent_inode; -+ } s2; -+ } u; -+ struct inode vfs_inode; -+}; -+#endif -Index: linux-2.6.22/include/linux/squashfs_fs_sb.h -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/include/linux/squashfs_fs_sb.h 2007-08-28 21:56:34.000000000 +0100 -@@ -0,0 +1,74 @@ -+#ifndef SQUASHFS_FS_SB -+#define SQUASHFS_FS_SB -+/* -+ * Squashfs -+ * -+ * Copyright (c) 2002, 2003, 2004, 2005, 2006 -+ * Phillip Lougher <phillip@lougher.org.uk> -+ * -+ * 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 the Free Software Foundation; either version 2, -+ * or (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -+ * -+ * squashfs_fs_sb.h -+ */ -+ -+#include <linux/squashfs_fs.h> -+ -+struct squashfs_cache { -+ long long block; -+ int length; -+ long long next_index; -+ char *data; -+}; -+ -+struct squashfs_fragment_cache { -+ long long block; -+ int length; -+ unsigned int locked; -+ char *data; -+}; -+ -+struct squashfs_sb_info { -+ struct squashfs_super_block sblk; -+ int devblksize; -+ int devblksize_log2; -+ int swap; -+ struct squashfs_cache *block_cache; -+ struct squashfs_fragment_cache *fragment; -+ int next_cache; -+ int next_fragment; -+ int next_meta_index; -+ unsigned int *uid; -+ unsigned int *guid; -+ long long *fragment_index; -+ unsigned int *fragment_index_2; -+ unsigned int read_size; -+ char *read_data; -+ char *read_page; -+ struct semaphore read_data_mutex; -+ struct semaphore read_page_mutex; -+ struct semaphore block_cache_mutex; -+ struct semaphore fragment_mutex; -+ struct semaphore meta_index_mutex; -+ wait_queue_head_t waitq; -+ wait_queue_head_t fragment_wait_queue; -+ struct meta_index *meta_index; -+ struct inode *(*iget)(struct super_block *s, squashfs_inode_t \ -+ inode); -+ long long (*read_blocklist)(struct inode *inode, int \ -+ index, int readahead_blks, char *block_list, \ -+ unsigned short **block_p, unsigned int *bsize); -+ int (*read_fragment_index_table)(struct super_block *s); -+}; -+#endif -Index: linux-2.6.22/init/do_mounts_rd.c -=================================================================== ---- linux-2.6.22.orig/init/do_mounts_rd.c 2007-08-28 21:54:14.000000000 +0100 -+++ linux-2.6.22/init/do_mounts_rd.c 2007-08-28 21:56:34.000000000 +0100 -@@ -5,6 +5,7 @@ - #include <linux/ext2_fs.h> - #include <linux/romfs_fs.h> - #include <linux/cramfs_fs.h> -+#include <linux/squashfs_fs.h> - #include <linux/initrd.h> - #include <linux/string.h> - -@@ -39,6 +40,7 @@ static int __init crd_load(int in_fd, in - * numbers could not be found. - * - * We currently check for the following magic numbers: -+ * squashfs - * minix - * ext2 - * romfs -@@ -53,6 +55,7 @@ identify_ramdisk_image(int fd, int start - struct ext2_super_block *ext2sb; - struct romfs_super_block *romfsb; - struct cramfs_super *cramfsb; -+ struct squashfs_super_block *squashfsb; - int nblocks = -1; - unsigned char *buf; - -@@ -64,6 +67,7 @@ identify_ramdisk_image(int fd, int start - ext2sb = (struct ext2_super_block *) buf; - romfsb = (struct romfs_super_block *) buf; - cramfsb = (struct cramfs_super *) buf; -+ squashfsb = (struct squashfs_super_block *) buf; - memset(buf, 0xe5, size); - - /* -@@ -101,6 +105,15 @@ identify_ramdisk_image(int fd, int start - goto done; - } - -+ /* squashfs is at block zero too */ -+ if (squashfsb->s_magic == SQUASHFS_MAGIC) { -+ printk(KERN_NOTICE -+ "RAMDISK: squashfs filesystem found at block %d\n", -+ start_block); -+ nblocks = (squashfsb->bytes_used+BLOCK_SIZE-1)>>BLOCK_SIZE_BITS; -+ goto done; -+ } -+ - /* - * Read block 1 to test for minix and ext2 superblock - */ diff --git a/meta/packages/linux/linux-rp-2.6.23+2.6.24-rc8/squashfs3.2-2.6.20-r0.patch b/meta/packages/linux/linux-rp-2.6.23+2.6.24-rc8/squashfs3.3.patch index 3870b317e4..cb9a5c49e4 100644 --- a/meta/packages/linux/linux-rp-2.6.23+2.6.24-rc8/squashfs3.2-2.6.20-r0.patch +++ b/meta/packages/linux/linux-rp-2.6.23+2.6.24-rc8/squashfs3.3.patch @@ -1,25 +1,27 @@ ---- linux-2.6.24-rc1.orig/fs/Kconfig -+++ linux-2.6.24-rc1/fs/Kconfig -@@ -1405,6 +1405,71 @@ +diff -x .gitignore -Nurp linux-2.6.24/fs/Kconfig linux-2.6.24-squashfs3.3/fs/Kconfig +--- linux-2.6.24/fs/Kconfig 2007-10-25 17:41:45.000000000 +0100 ++++ linux-2.6.24-squashfs3.3/fs/Kconfig 2007-11-01 05:06:25.000000000 +0000 +@@ -1396,6 +1396,56 @@ config CRAMFS If unsure, say N. +config SQUASHFS -+ tristate "SquashFS 3.2 - Squashed file system support" ++ tristate "SquashFS 3.3 - Squashed file system support" + select ZLIB_INFLATE + help -+ Saying Y here includes support for SquashFS 3.2 (a Compressed Read-Only File -+ System). Squashfs is a highly compressed read-only filesystem for Linux. -+ It uses zlib compression to compress both files, inodes and directories. -+ Inodes in the system are very small and all blocks are packed to minimise -+ data overhead. Block sizes greater than 4K are supported up to a maximum of 64K. -+ SquashFS 3.1 supports 64 bit filesystems and files (larger than 4GB), full -+ uid/gid information, hard links and timestamps. -+ -+ Squashfs is intended for general read-only filesystem use, for archival -+ use (i.e. in cases where a .tar.gz file may be used), and in embedded -+ systems where low overhead is needed. Further information and filesystem tools -+ are available from http://squashfs.sourceforge.net. ++ Saying Y here includes support for SquashFS 3.3 (a Compressed ++ Read-Only File System). Squashfs is a highly compressed read-only ++ filesystem for Linux. It uses zlib compression to compress both ++ files, inodes and directories. Inodes in the system are very small ++ and all blocks are packed to minimise data overhead. Block sizes ++ greater than 4K are supported up to a maximum of 1 Mbytes (default ++ block size 128K). SquashFS 3.3 supports 64 bit filesystems and files ++ (larger than 4GB), full uid/gid information, hard links and timestamps. ++ ++ Squashfs is intended for general read-only filesystem use, for ++ archival use (i.e. in cases where a .tar.gz file may be used), and in ++ embedded systems where low overhead is needed. Further information ++ and filesystem tools are available from http://squashfs.sourceforge.net. + + If you want to compile this as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), @@ -31,13 +33,11 @@ + +config SQUASHFS_EMBEDDED + -+ bool "Additional options for memory-constrained systems" ++ bool "Additional option for memory-constrained systems" + depends on SQUASHFS + default n + help -+ Saying Y here allows you to specify cache sizes and how Squashfs -+ allocates memory. This is only intended for memory constrained -+ systems. ++ Saying Y here allows you to specify cache size. + + If unsure, say N. + @@ -55,26 +55,13 @@ + Note there must be at least one cached fragment. Anything + much more than three will probably not make much difference. + -+config SQUASHFS_VMALLOC -+ bool "Use Vmalloc rather than Kmalloc" if SQUASHFS_EMBEDDED -+ depends on SQUASHFS -+ default n -+ help -+ By default SquashFS uses kmalloc to obtain fragment cache memory. -+ Kmalloc memory is the standard kernel allocator, but it can fail -+ on memory constrained systems. Because of the way Vmalloc works, -+ Vmalloc can succeed when kmalloc fails. Specifying this option -+ will make SquashFS always use Vmalloc to allocate the -+ fragment cache memory. -+ -+ If unsure, say N. -+ config VXFS_FS tristate "FreeVxFS file system support (VERITAS VxFS(TM) compatible)" depends on BLOCK ---- linux-2.6.24-rc1.orig/fs/Makefile -+++ linux-2.6.24-rc1/fs/Makefile -@@ -72,6 +72,7 @@ +diff -x .gitignore -Nurp linux-2.6.24/fs/Makefile linux-2.6.24-squashfs3.3/fs/Makefile +--- linux-2.6.24/fs/Makefile 2007-10-25 17:41:45.000000000 +0100 ++++ linux-2.6.24-squashfs3.3/fs/Makefile 2007-11-01 05:08:09.000000000 +0000 +@@ -72,6 +72,7 @@ obj-$(CONFIG_JBD) += jbd/ obj-$(CONFIG_JBD2) += jbd2/ obj-$(CONFIG_EXT2_FS) += ext2/ obj-$(CONFIG_CRAMFS) += cramfs/ @@ -82,14 +69,15 @@ obj-y += ramfs/ obj-$(CONFIG_HUGETLBFS) += hugetlbfs/ obj-$(CONFIG_CODA_FS) += coda/ ---- /dev/null -+++ linux-2.6.24-rc1/fs/squashfs/inode.c -@@ -0,0 +1,2329 @@ +diff -x .gitignore -Nurp linux-2.6.24/fs/squashfs/inode.c linux-2.6.24-squashfs3.3/fs/squashfs/inode.c +--- linux-2.6.24/fs/squashfs/inode.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24-squashfs3.3/fs/squashfs/inode.c 2007-11-01 05:05:00.000000000 +0000 +@@ -0,0 +1,2192 @@ +/* + * Squashfs - a compressed read only filesystem for Linux + * + * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007 -+ * Phillip Lougher <phillip@lougher.org.uk> ++ * Phillip Lougher <phillip@lougher.demon.co.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License @@ -118,9 +106,12 @@ +#include <linux/vfs.h> +#include <linux/vmalloc.h> +#include <linux/smp_lock.h> ++#include <linux/exportfs.h> + +#include "squashfs.h" + ++int squashfs_cached_blks; ++ +static void vfs_read_inode(struct inode *i); +static struct dentry *squashfs_get_parent(struct dentry *child); +static int squashfs_read_inode(struct inode *i, squashfs_inode_t inode); @@ -130,7 +121,6 @@ + int readahead_blks, char *block_list, + unsigned short **block_p, unsigned int *bsize); +static int squashfs_readpage(struct file *file, struct page *page); -+static int squashfs_readpage4K(struct file *file, struct page *page); +static int squashfs_readdir(struct file *, void *, filldir_t); +static struct dentry *squashfs_lookup(struct inode *, struct dentry *, + struct nameidata *); @@ -183,10 +173,6 @@ + .readpage = squashfs_readpage +}; + -+SQSH_EXTERN const struct address_space_operations squashfs_aops_4K = { -+ .readpage = squashfs_readpage4K -+}; -+ +static const struct file_operations squashfs_dir_ops = { + .read = generic_read_dir, + .readdir = squashfs_readdir @@ -270,30 +256,36 @@ +{ + struct squashfs_sb_info *msblk = s->s_fs_info; + struct squashfs_super_block *sblk = &msblk->sblk; -+ struct buffer_head *bh[((SQUASHFS_FILE_MAX_SIZE - 1) >> -+ msblk->devblksize_log2) + 2]; ++ struct buffer_head **bh; + unsigned int offset = index & ((1 << msblk->devblksize_log2) - 1); + unsigned int cur_index = index >> msblk->devblksize_log2; + int bytes, avail_bytes, b = 0, k = 0; + unsigned int compressed; + unsigned int c_byte = length; + ++ bh = kmalloc(((sblk->block_size >> msblk->devblksize_log2) + 1) * ++ sizeof(struct buffer_head *), GFP_KERNEL); ++ if (bh == NULL) ++ goto read_failure; ++ + if (c_byte) { + bytes = msblk->devblksize - offset; + compressed = SQUASHFS_COMPRESSED_BLOCK(c_byte); + c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte); + -+ TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n", index, compressed -+ ? "" : "un", (unsigned int) c_byte, srclength); ++ TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n", index, ++ compressed ? "" : "un", (unsigned int) c_byte, srclength); + + if (c_byte > srclength || index < 0 || (index + c_byte) > sblk->bytes_used) + goto read_failure; + -+ if (!(bh[0] = sb_getblk(s, cur_index))) ++ bh[0] = sb_getblk(s, cur_index); ++ if (bh[0] == NULL) + goto block_release; + + for (b = 1; bytes < c_byte; b++) { -+ if (!(bh[b] = sb_getblk(s, ++cur_index))) ++ bh[b] = sb_getblk(s, ++cur_index); ++ if (bh[b] == NULL) + goto block_release; + bytes += msblk->devblksize; + } @@ -302,8 +294,8 @@ + if (index < 0 || (index + 2) > sblk->bytes_used) + goto read_failure; + -+ if (!(bh[0] = get_block_length(s, &cur_index, &offset, -+ &c_byte))) ++ bh[0] = get_block_length(s, &cur_index, &offset, &c_byte); ++ if (bh[0] == NULL) + goto read_failure; + + bytes = msblk->devblksize - offset; @@ -317,7 +309,8 @@ + goto read_failure; + + for (b = 1; bytes < c_byte; b++) { -+ if (!(bh[b] = sb_getblk(s, ++cur_index))) ++ bh[b] = sb_getblk(s, ++cur_index); ++ if (bh[b] == NULL) + goto block_release; + bytes += msblk->devblksize; + } @@ -337,9 +330,8 @@ + msblk->stream.avail_out = srclength; + + for (bytes = 0; k < b; k++) { -+ avail_bytes = (c_byte - bytes) > (msblk->devblksize - offset) ? -+ msblk->devblksize - offset : -+ c_byte - bytes; ++ avail_bytes = min(c_byte - bytes, msblk->devblksize - offset); ++ + wait_on_buffer(bh[k]); + if (!buffer_uptodate(bh[k])) + goto release_mutex; @@ -350,8 +342,8 @@ + if (k == 0) { + zlib_err = zlib_inflateInit(&msblk->stream); + if (zlib_err != Z_OK) { -+ ERROR("zlib_inflateInit returned unexpected result 0x%x, srclength %d\n", -+ zlib_err, srclength); ++ ERROR("zlib_inflateInit returned unexpected result 0x%x," ++ " srclength %d\n", zlib_err, srclength); + goto release_mutex; + } + @@ -364,8 +356,9 @@ + + zlib_err = zlib_inflate(&msblk->stream, Z_NO_FLUSH); + if (zlib_err != Z_OK && zlib_err != Z_STREAM_END) { -+ ERROR("zlib_inflate returned unexpected result 0x%x, srclength %d, avail_in %d, avail_out %d\n", -+ zlib_err, srclength, msblk->stream.avail_in, msblk->stream.avail_out); ++ ERROR("zlib_inflate returned unexpected result 0x%x," ++ " srclength %d, avail_in %d, avail_out %d\n", zlib_err, ++ srclength, msblk->stream.avail_in, msblk->stream.avail_out); + goto release_mutex; + } + @@ -379,8 +372,8 @@ + + zlib_err = zlib_inflateEnd(&msblk->stream); + if (zlib_err != Z_OK) { -+ ERROR("zlib_inflateEnd returned unexpected result 0x%x, srclength %d\n", -+ zlib_err, srclength); ++ ERROR("zlib_inflateEnd returned unexpected result 0x%x," ++ " srclength %d\n", zlib_err, srclength); + goto release_mutex; + } + bytes = msblk->stream.total_out; @@ -390,14 +383,13 @@ + + for(i = 0; i < b; i++) { + wait_on_buffer(bh[i]); -+ if(!buffer_uptodate(bh[i])) ++ if (!buffer_uptodate(bh[i])) + goto block_release; + } + + for (bytes = 0; k < b; k++) { -+ avail_bytes = (c_byte - bytes) > (msblk->devblksize - offset) ? -+ msblk->devblksize - offset : -+ c_byte - bytes; ++ avail_bytes = min(c_byte - bytes, msblk->devblksize - offset); ++ + memcpy(buffer + bytes, bh[k]->b_data + offset, avail_bytes); + bytes += avail_bytes; + offset = 0; @@ -407,8 +399,9 @@ + + if (next_index) + *next_index = index + c_byte + (length ? 0 : -+ (SQUASHFS_CHECK_DATA(msblk->sblk.flags) -+ ? 3 : 2)); ++ (SQUASHFS_CHECK_DATA(msblk->sblk.flags) ? 3 : 2)); ++ ++ kfree(bh); + return bytes; + +release_mutex: @@ -420,11 +413,12 @@ + +read_failure: + ERROR("sb_bread failed reading block 0x%x\n", cur_index); ++ kfree(bh); + return 0; +} + + -+SQSH_EXTERN int squashfs_get_cached_block(struct super_block *s, char *buffer, ++SQSH_EXTERN int squashfs_get_cached_block(struct super_block *s, void *buffer, + long long block, unsigned int offset, + int length, long long *next_block, + unsigned int *next_offset) @@ -435,68 +429,65 @@ + + TRACE("Entered squashfs_get_cached_block [%llx:%x]\n", block, offset); + -+ while ( 1 ) { -+ for (i = 0; i < SQUASHFS_CACHED_BLKS; i++) ++ while (1) { ++ for (i = 0; i < squashfs_cached_blks; i++) + if (msblk->block_cache[i].block == block) + break; + + mutex_lock(&msblk->block_cache_mutex); + -+ if (i == SQUASHFS_CACHED_BLKS) { ++ if (i == squashfs_cached_blks) { + /* read inode header block */ -+ for (i = msblk->next_cache, n = SQUASHFS_CACHED_BLKS; -+ n ; n --, i = (i + 1) % -+ SQUASHFS_CACHED_BLKS) -+ if (msblk->block_cache[i].block != -+ SQUASHFS_USED_BLK) ++ if (msblk->unused_cache_blks == 0) { ++ mutex_unlock(&msblk->block_cache_mutex); ++ wait_event(msblk->waitq, msblk->unused_cache_blks); ++ continue; ++ } ++ ++ i = msblk->next_cache; ++ for (n = 0; n < squashfs_cached_blks; n++) { ++ if (msblk->block_cache[i].block != SQUASHFS_USED_BLK) + break; ++ i = (i + 1) % squashfs_cached_blks; ++ } + -+ if (n == 0) { -+ wait_queue_t wait; ++ msblk->next_cache = (i + 1) % squashfs_cached_blks; + -+ init_waitqueue_entry(&wait, current); -+ add_wait_queue(&msblk->waitq, &wait); -+ set_current_state(TASK_UNINTERRUPTIBLE); -+ mutex_unlock(&msblk->block_cache_mutex); -+ schedule(); -+ set_current_state(TASK_RUNNING); -+ remove_wait_queue(&msblk->waitq, &wait); -+ continue; -+ } -+ msblk->next_cache = (i + 1) % SQUASHFS_CACHED_BLKS; -+ -+ if (msblk->block_cache[i].block == -+ SQUASHFS_INVALID_BLK) { -+ if (!(msblk->block_cache[i].data = -+ kmalloc(SQUASHFS_METADATA_SIZE, -+ GFP_KERNEL))) { -+ ERROR("Failed to allocate cache" -+ "block\n"); ++ if (msblk->block_cache[i].block == SQUASHFS_INVALID_BLK) { ++ msblk->block_cache[i].data = vmalloc(SQUASHFS_METADATA_SIZE); ++ if (msblk->block_cache[i].data == NULL) { ++ ERROR("Failed to allocate cache block\n"); + mutex_unlock(&msblk->block_cache_mutex); + goto out; + } + } + + msblk->block_cache[i].block = SQUASHFS_USED_BLK; ++ msblk->unused_cache_blks --; + mutex_unlock(&msblk->block_cache_mutex); + + msblk->block_cache[i].length = squashfs_read_data(s, -+ msblk->block_cache[i].data, block, 0, &next_index, SQUASHFS_METADATA_SIZE); ++ msblk->block_cache[i].data, block, 0, &next_index, ++ SQUASHFS_METADATA_SIZE); ++ + if (msblk->block_cache[i].length == 0) { -+ ERROR("Unable to read cache block [%llx:%x]\n", -+ block, offset); ++ ERROR("Unable to read cache block [%llx:%x]\n", block, offset); + mutex_lock(&msblk->block_cache_mutex); + msblk->block_cache[i].block = SQUASHFS_INVALID_BLK; -+ kfree(msblk->block_cache[i].data); ++ msblk->unused_cache_blks ++; ++ smp_mb(); ++ vfree(msblk->block_cache[i].data); + wake_up(&msblk->waitq); + mutex_unlock(&msblk->block_cache_mutex); + goto out; + } + + mutex_lock(&msblk->block_cache_mutex); -+ wake_up(&msblk->waitq); + msblk->block_cache[i].block = block; + msblk->block_cache[i].next_index = next_index; ++ msblk->unused_cache_blks ++; ++ smp_mb(); ++ wake_up(&msblk->waitq); + TRACE("Read cache block [%llx:%x]\n", block, offset); + } + @@ -512,8 +503,7 @@ + goto out; + } else if (bytes >= length) { + if (buffer) -+ memcpy(buffer, msblk->block_cache[i].data + -+ offset, length); ++ memcpy(buffer, msblk->block_cache[i].data + offset, length); + if (msblk->block_cache[i].length - offset == length) { + *next_block = msblk->block_cache[i].next_index; + *next_offset = 0; @@ -525,9 +515,8 @@ + goto finish; + } else { + if (buffer) { -+ memcpy(buffer, msblk->block_cache[i].data + -+ offset, bytes); -+ buffer += bytes; ++ memcpy(buffer, msblk->block_cache[i].data + offset, bytes); ++ buffer = (char *) buffer + bytes; + } + block = msblk->block_cache[i].next_index; + mutex_unlock(&msblk->block_cache_mutex); @@ -556,17 +545,13 @@ + if (msblk->swap) { + struct squashfs_fragment_entry sfragment_entry; + -+ if (!squashfs_get_cached_block(s, (char *) &sfragment_entry, -+ start_block, offset, -+ sizeof(sfragment_entry), &start_block, -+ &offset)) ++ if (!squashfs_get_cached_block(s, &sfragment_entry, start_block, offset, ++ sizeof(sfragment_entry), &start_block, &offset)) + goto out; + SQUASHFS_SWAP_FRAGMENT_ENTRY(&fragment_entry, &sfragment_entry); + } else -+ if (!squashfs_get_cached_block(s, (char *) &fragment_entry, -+ start_block, offset, -+ sizeof(fragment_entry), &start_block, -+ &offset)) ++ if (!squashfs_get_cached_block(s, &fragment_entry, start_block, offset, ++ sizeof(fragment_entry), &start_block, &offset)) + goto out; + + *fragment_start_block = fragment_entry.start_block; @@ -579,90 +564,92 @@ +} + + -+SQSH_EXTERN void release_cached_fragment(struct squashfs_sb_info *msblk, struct -+ squashfs_fragment_cache *fragment) ++SQSH_EXTERN void release_cached_fragment(struct squashfs_sb_info *msblk, ++ struct squashfs_fragment_cache *fragment) +{ + mutex_lock(&msblk->fragment_mutex); + fragment->locked --; -+ wake_up(&msblk->fragment_wait_queue); ++ if (fragment->locked == 0) { ++ msblk->unused_frag_blks ++; ++ smp_mb(); ++ wake_up(&msblk->fragment_wait_queue); ++ } + mutex_unlock(&msblk->fragment_mutex); +} + + -+SQSH_EXTERN struct squashfs_fragment_cache *get_cached_fragment(struct super_block -+ *s, long long start_block, -+ int length) ++SQSH_EXTERN ++struct squashfs_fragment_cache *get_cached_fragment(struct super_block *s, ++ long long start_block, int length) +{ + int i, n; + struct squashfs_sb_info *msblk = s->s_fs_info; + struct squashfs_super_block *sblk = &msblk->sblk; + -+ while ( 1 ) { ++ while (1) { + mutex_lock(&msblk->fragment_mutex); + + for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS && + msblk->fragment[i].block != start_block; i++); + + if (i == SQUASHFS_CACHED_FRAGMENTS) { -+ for (i = msblk->next_fragment, n = -+ SQUASHFS_CACHED_FRAGMENTS; n && -+ msblk->fragment[i].locked; n--, i = (i + 1) % -+ SQUASHFS_CACHED_FRAGMENTS); -+ -+ if (n == 0) { -+ wait_queue_t wait; -+ -+ init_waitqueue_entry(&wait, current); -+ add_wait_queue(&msblk->fragment_wait_queue, -+ &wait); -+ set_current_state(TASK_UNINTERRUPTIBLE); ++ if (msblk->unused_frag_blks == 0) { + mutex_unlock(&msblk->fragment_mutex); -+ schedule(); -+ set_current_state(TASK_RUNNING); -+ remove_wait_queue(&msblk->fragment_wait_queue, -+ &wait); ++ wait_event(msblk->fragment_wait_queue, msblk->unused_frag_blks); + continue; + } ++ ++ i = msblk->next_fragment; ++ for (n = 0; n < SQUASHFS_CACHED_FRAGMENTS; n++) { ++ if (msblk->fragment[i].locked == 0) ++ break; ++ i = (i + 1) % SQUASHFS_CACHED_FRAGMENTS; ++ } ++ + msblk->next_fragment = (msblk->next_fragment + 1) % + SQUASHFS_CACHED_FRAGMENTS; + -+ if (msblk->fragment[i].data == NULL) -+ if (!(msblk->fragment[i].data = SQUASHFS_ALLOC -+ (SQUASHFS_FILE_MAX_SIZE))) { -+ ERROR("Failed to allocate fragment " -+ "cache block\n"); ++ if (msblk->fragment[i].data == NULL) { ++ msblk->fragment[i].data = vmalloc(sblk->block_size); ++ if (msblk->fragment[i].data == NULL) { ++ ERROR("Failed to allocate fragment cache block\n"); + mutex_unlock(&msblk->fragment_mutex); + goto out; + } ++ } + ++ msblk->unused_frag_blks --; + msblk->fragment[i].block = SQUASHFS_INVALID_BLK; + msblk->fragment[i].locked = 1; + mutex_unlock(&msblk->fragment_mutex); + -+ if (!(msblk->fragment[i].length = squashfs_read_data(s, -+ msblk->fragment[i].data, -+ start_block, length, NULL, sblk->block_size))) { -+ ERROR("Unable to read fragment cache block " -+ "[%llx]\n", start_block); ++ msblk->fragment[i].length = squashfs_read_data(s, ++ msblk->fragment[i].data, start_block, length, NULL, ++ sblk->block_size); ++ ++ if (msblk->fragment[i].length == 0) { ++ ERROR("Unable to read fragment cache block [%llx]\n", start_block); + msblk->fragment[i].locked = 0; ++ msblk->unused_frag_blks ++; + smp_mb(); ++ wake_up(&msblk->fragment_wait_queue); + goto out; + } + + mutex_lock(&msblk->fragment_mutex); + msblk->fragment[i].block = start_block; + TRACE("New fragment %d, start block %lld, locked %d\n", -+ i, msblk->fragment[i].block, -+ msblk->fragment[i].locked); ++ i, msblk->fragment[i].block, msblk->fragment[i].locked); + mutex_unlock(&msblk->fragment_mutex); + break; + } + ++ if (msblk->fragment[i].locked == 0) ++ msblk->unused_frag_blks --; + msblk->fragment[i].locked++; + mutex_unlock(&msblk->fragment_mutex); + TRACE("Got fragment %d, start block %lld, locked %d\n", i, -+ msblk->fragment[i].block, -+ msblk->fragment[i].locked); ++ msblk->fragment[i].block, msblk->fragment[i].locked); + break; + } + @@ -674,7 +661,7 @@ + + +static void squashfs_new_inode(struct squashfs_sb_info *msblk, struct inode *i, -+ struct squashfs_base_inode_header *inodeb) ++ struct squashfs_base_inode_header *inodeb) +{ + i->i_ino = inodeb->inode_number; + i->i_mtime.tv_sec = inodeb->mtime; @@ -683,6 +670,7 @@ + i->i_uid = msblk->uid[inodeb->uid]; + i->i_mode = inodeb->mode; + i->i_size = 0; ++ + if (inodeb->guid == SQUASHFS_GUIDS) + i->i_gid = i->i_uid; + else @@ -702,11 +690,11 @@ + if (msblk->swap) { + squashfs_inode_t sinode; + -+ if (!squashfs_get_cached_block(s, (char *) &sinode, start, offset, ++ if (!squashfs_get_cached_block(s, &sinode, start, offset, + sizeof(sinode), &start, &offset)) + goto out; + SQUASHFS_SWAP_INODE_T((&inode), &sinode); -+ } else if (!squashfs_get_cached_block(s, (char *) &inode, start, offset, ++ } else if (!squashfs_get_cached_block(s, &inode, start, offset, + sizeof(inode), &start, &offset)) + goto out; + @@ -753,7 +741,8 @@ +} + + -+SQSH_EXTERN struct inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode, unsigned int inode_number) ++SQSH_EXTERN struct inode *squashfs_iget(struct super_block *s, ++ squashfs_inode_t inode, unsigned int inode_number) +{ + struct squashfs_sb_info *msblk = s->s_fs_info; + struct inode *i = iget_locked(s, inode_number); @@ -774,28 +763,23 @@ + struct super_block *s = i->i_sb; + struct squashfs_sb_info *msblk = s->s_fs_info; + struct squashfs_super_block *sblk = &msblk->sblk; -+ long long block = SQUASHFS_INODE_BLK(inode) + -+ sblk->inode_table_start; ++ long long block = SQUASHFS_INODE_BLK(inode) + sblk->inode_table_start; + unsigned int offset = SQUASHFS_INODE_OFFSET(inode); + long long next_block; + unsigned int next_offset; + union squashfs_inode_header id, sid; -+ struct squashfs_base_inode_header *inodeb = &id.base, -+ *sinodeb = &sid.base; ++ struct squashfs_base_inode_header *inodeb = &id.base, *sinodeb = &sid.base; + + TRACE("Entered squashfs_read_inode\n"); + + if (msblk->swap) { -+ if (!squashfs_get_cached_block(s, (char *) sinodeb, block, -+ offset, sizeof(*sinodeb), &next_block, -+ &next_offset)) ++ if (!squashfs_get_cached_block(s, sinodeb, block, offset, ++ sizeof(*sinodeb), &next_block, &next_offset)) + goto failed_read; -+ SQUASHFS_SWAP_BASE_INODE_HEADER(inodeb, sinodeb, -+ sizeof(*sinodeb)); ++ SQUASHFS_SWAP_BASE_INODE_HEADER(inodeb, sinodeb, sizeof(*sinodeb)); + } else -+ if (!squashfs_get_cached_block(s, (char *) inodeb, block, -+ offset, sizeof(*inodeb), &next_block, -+ &next_offset)) ++ if (!squashfs_get_cached_block(s, inodeb, block, offset, ++ sizeof(*inodeb), &next_block, &next_offset)) + goto failed_read; + + squashfs_new_inode(msblk, i, inodeb); @@ -808,24 +792,21 @@ + struct squashfs_reg_inode_header *sinodep = &sid.reg; + + if (msblk->swap) { -+ if (!squashfs_get_cached_block(s, (char *) -+ sinodep, block, offset, -+ sizeof(*sinodep), &next_block, -+ &next_offset)) ++ if (!squashfs_get_cached_block(s, sinodep, block, offset, ++ sizeof(*sinodep), &next_block, &next_offset)) + goto failed_read; + SQUASHFS_SWAP_REG_INODE_HEADER(inodep, sinodep); + } else -+ if (!squashfs_get_cached_block(s, (char *) -+ inodep, block, offset, -+ sizeof(*inodep), &next_block, -+ &next_offset)) ++ if (!squashfs_get_cached_block(s, inodep, block, offset, ++ sizeof(*inodep), &next_block, &next_offset)) + goto failed_read; + + frag_blk = SQUASHFS_INVALID_BLK; -+ if (inodep->fragment != SQUASHFS_INVALID_FRAG && -+ !get_fragment_location(s, -+ inodep->fragment, &frag_blk, &frag_size)) -+ goto failed_read; ++ ++ if (inodep->fragment != SQUASHFS_INVALID_FRAG) ++ if(!get_fragment_location(s, inodep->fragment, &frag_blk, ++ &frag_size)) ++ goto failed_read; + + i->i_nlink = 1; + i->i_size = inodep->file_size; @@ -838,10 +819,7 @@ + SQUASHFS_I(i)->start_block = inodep->start_block; + SQUASHFS_I(i)->u.s1.block_list_start = next_block; + SQUASHFS_I(i)->offset = next_offset; -+ if (sblk->block_size > 4096) -+ i->i_data.a_ops = &squashfs_aops; -+ else -+ i->i_data.a_ops = &squashfs_aops_4K; ++ i->i_data.a_ops = &squashfs_aops; + + TRACE("File inode %x:%x, start_block %llx, " + "block_list_start %llx, offset %x\n", @@ -857,24 +835,21 @@ + struct squashfs_lreg_inode_header *sinodep = &sid.lreg; + + if (msblk->swap) { -+ if (!squashfs_get_cached_block(s, (char *) -+ sinodep, block, offset, -+ sizeof(*sinodep), &next_block, -+ &next_offset)) ++ if (!squashfs_get_cached_block(s, sinodep, block, offset, ++ sizeof(*sinodep), &next_block, &next_offset)) + goto failed_read; + SQUASHFS_SWAP_LREG_INODE_HEADER(inodep, sinodep); + } else -+ if (!squashfs_get_cached_block(s, (char *) -+ inodep, block, offset, -+ sizeof(*inodep), &next_block, -+ &next_offset)) ++ if (!squashfs_get_cached_block(s, inodep, block, offset, ++ sizeof(*inodep), &next_block, &next_offset)) + goto failed_read; + + frag_blk = SQUASHFS_INVALID_BLK; -+ if (inodep->fragment != SQUASHFS_INVALID_FRAG && -+ !get_fragment_location(s, -+ inodep->fragment, &frag_blk, &frag_size)) -+ goto failed_read; ++ ++ if (inodep->fragment != SQUASHFS_INVALID_FRAG) ++ if (!get_fragment_location(s, inodep->fragment, &frag_blk, ++ &frag_size)) ++ goto failed_read; + + i->i_nlink = inodep->nlink; + i->i_size = inodep->file_size; @@ -887,10 +862,7 @@ + SQUASHFS_I(i)->start_block = inodep->start_block; + SQUASHFS_I(i)->u.s1.block_list_start = next_block; + SQUASHFS_I(i)->offset = next_offset; -+ if (sblk->block_size > 4096) -+ i->i_data.a_ops = &squashfs_aops; -+ else -+ i->i_data.a_ops = &squashfs_aops_4K; ++ i->i_data.a_ops = &squashfs_aops; + + TRACE("File inode %x:%x, start_block %llx, " + "block_list_start %llx, offset %x\n", @@ -904,17 +876,13 @@ + struct squashfs_dir_inode_header *sinodep = &sid.dir; + + if (msblk->swap) { -+ if (!squashfs_get_cached_block(s, (char *) -+ sinodep, block, offset, -+ sizeof(*sinodep), &next_block, -+ &next_offset)) ++ if (!squashfs_get_cached_block(s, sinodep, block, offset, ++ sizeof(*sinodep), &next_block, &next_offset)) + goto failed_read; + SQUASHFS_SWAP_DIR_INODE_HEADER(inodep, sinodep); + } else -+ if (!squashfs_get_cached_block(s, (char *) -+ inodep, block, offset, -+ sizeof(*inodep), &next_block, -+ &next_offset)) ++ if (!squashfs_get_cached_block(s, inodep, block, offset, ++ sizeof(*inodep), &next_block, &next_offset)) + goto failed_read; + + i->i_nlink = inodep->nlink; @@ -938,18 +906,13 @@ + struct squashfs_ldir_inode_header *sinodep = &sid.ldir; + + if (msblk->swap) { -+ if (!squashfs_get_cached_block(s, (char *) -+ sinodep, block, offset, -+ sizeof(*sinodep), &next_block, -+ &next_offset)) ++ if (!squashfs_get_cached_block(s, sinodep, block, offset, ++ sizeof(*sinodep), &next_block, &next_offset)) + goto failed_read; -+ SQUASHFS_SWAP_LDIR_INODE_HEADER(inodep, -+ sinodep); ++ SQUASHFS_SWAP_LDIR_INODE_HEADER(inodep, sinodep); + } else -+ if (!squashfs_get_cached_block(s, (char *) -+ inodep, block, offset, -+ sizeof(*inodep), &next_block, -+ &next_offset)) ++ if (!squashfs_get_cached_block(s, inodep, block, offset, ++ sizeof(*inodep), &next_block, &next_offset)) + goto failed_read; + + i->i_nlink = inodep->nlink; @@ -960,37 +923,27 @@ + SQUASHFS_I(i)->start_block = inodep->start_block; + SQUASHFS_I(i)->offset = inodep->offset; + SQUASHFS_I(i)->u.s2.directory_index_start = next_block; -+ SQUASHFS_I(i)->u.s2.directory_index_offset = -+ next_offset; -+ SQUASHFS_I(i)->u.s2.directory_index_count = -+ inodep->i_count; ++ SQUASHFS_I(i)->u.s2.directory_index_offset = next_offset; ++ SQUASHFS_I(i)->u.s2.directory_index_count = inodep->i_count; + SQUASHFS_I(i)->u.s2.parent_inode = inodep->parent_inode; + -+ TRACE("Long directory inode %x:%x, start_block %x, " -+ "offset %x\n", ++ TRACE("Long directory inode %x:%x, start_block %x, offset %x\n", + SQUASHFS_INODE_BLK(inode), offset, + inodep->start_block, inodep->offset); + break; + } + case SQUASHFS_SYMLINK_TYPE: { -+ struct squashfs_symlink_inode_header *inodep = -+ &id.symlink; -+ struct squashfs_symlink_inode_header *sinodep = -+ &sid.symlink; ++ struct squashfs_symlink_inode_header *inodep = &id.symlink; ++ struct squashfs_symlink_inode_header *sinodep = &sid.symlink; + + if (msblk->swap) { -+ if (!squashfs_get_cached_block(s, (char *) -+ sinodep, block, offset, -+ sizeof(*sinodep), &next_block, -+ &next_offset)) ++ if (!squashfs_get_cached_block(s, sinodep, block, offset, ++ sizeof(*sinodep), &next_block, &next_offset)) + goto failed_read; -+ SQUASHFS_SWAP_SYMLINK_INODE_HEADER(inodep, -+ sinodep); ++ SQUASHFS_SWAP_SYMLINK_INODE_HEADER(inodep, sinodep); + } else -+ if (!squashfs_get_cached_block(s, (char *) -+ inodep, block, offset, -+ sizeof(*inodep), &next_block, -+ &next_offset)) ++ if (!squashfs_get_cached_block(s, inodep, block, offset, ++ sizeof(*inodep), &next_block, &next_offset)) + goto failed_read; + + i->i_nlink = inodep->nlink; @@ -1001,8 +954,7 @@ + SQUASHFS_I(i)->start_block = next_block; + SQUASHFS_I(i)->offset = next_offset; + -+ TRACE("Symbolic link inode %x:%x, start_block %llx, " -+ "offset %x\n", ++ TRACE("Symbolic link inode %x:%x, start_block %llx, offset %x\n", + SQUASHFS_INODE_BLK(inode), offset, + next_block, next_offset); + break; @@ -1013,29 +965,22 @@ + struct squashfs_dev_inode_header *sinodep = &sid.dev; + + if (msblk->swap) { -+ if (!squashfs_get_cached_block(s, (char *) -+ sinodep, block, offset, -+ sizeof(*sinodep), &next_block, -+ &next_offset)) ++ if (!squashfs_get_cached_block(s, sinodep, block, offset, ++ sizeof(*sinodep), &next_block, &next_offset)) + goto failed_read; + SQUASHFS_SWAP_DEV_INODE_HEADER(inodep, sinodep); + } else -+ if (!squashfs_get_cached_block(s, (char *) -+ inodep, block, offset, -+ sizeof(*inodep), &next_block, -+ &next_offset)) ++ if (!squashfs_get_cached_block(s, inodep, block, offset, ++ sizeof(*inodep), &next_block, &next_offset)) + goto failed_read; + + i->i_nlink = inodep->nlink; -+ i->i_mode |= (inodeb->inode_type == -+ SQUASHFS_CHRDEV_TYPE) ? S_IFCHR : -+ S_IFBLK; -+ init_special_inode(i, i->i_mode, -+ old_decode_dev(inodep->rdev)); ++ i->i_mode |= (inodeb->inode_type == SQUASHFS_CHRDEV_TYPE) ? ++ S_IFCHR : S_IFBLK; ++ init_special_inode(i, i->i_mode, old_decode_dev(inodep->rdev)); + + TRACE("Device inode %x:%x, rdev %x\n", -+ SQUASHFS_INODE_BLK(inode), offset, -+ inodep->rdev); ++ SQUASHFS_INODE_BLK(inode), offset, inodep->rdev); + break; + } + case SQUASHFS_FIFO_TYPE: @@ -1044,17 +989,13 @@ + struct squashfs_ipc_inode_header *sinodep = &sid.ipc; + + if (msblk->swap) { -+ if (!squashfs_get_cached_block(s, (char *) -+ sinodep, block, offset, -+ sizeof(*sinodep), &next_block, -+ &next_offset)) ++ if (!squashfs_get_cached_block(s, sinodep, block, offset, ++ sizeof(*sinodep), &next_block, &next_offset)) + goto failed_read; + SQUASHFS_SWAP_IPC_INODE_HEADER(inodep, sinodep); + } else -+ if (!squashfs_get_cached_block(s, (char *) -+ inodep, block, offset, -+ sizeof(*inodep), &next_block, -+ &next_offset)) ++ if (!squashfs_get_cached_block(s, inodep, block, offset, ++ sizeof(*inodep), &next_block, &next_offset)) + goto failed_read; + + i->i_nlink = inodep->nlink; @@ -1089,7 +1030,8 @@ + TRACE("In read_inode_lookup_table, length %d\n", length); + + /* Allocate inode lookup table */ -+ if (!(msblk->inode_lookup_table = kmalloc(length, GFP_KERNEL))) { ++ msblk->inode_lookup_table = kmalloc(length, GFP_KERNEL); ++ if (msblk->inode_lookup_table == NULL) { + ERROR("Failed to allocate inode lookup table\n"); + return 0; + } @@ -1106,6 +1048,7 @@ + long long block; + + for (i = 0; i < SQUASHFS_LOOKUP_BLOCKS(sblk->inodes); i++) { ++ /* XXX */ + SQUASHFS_SWAP_LOOKUP_BLOCKS((&block), + &msblk->inode_lookup_table[i], 1); + msblk->inode_lookup_table[i] = block; @@ -1126,7 +1069,8 @@ + return 1; + + /* Allocate fragment index table */ -+ if (!(msblk->fragment_index = kmalloc(length, GFP_KERNEL))) { ++ msblk->fragment_index = kmalloc(length, GFP_KERNEL); ++ if (msblk->fragment_index == NULL) { + ERROR("Failed to allocate fragment index table\n"); + return 0; + } @@ -1143,6 +1087,7 @@ + long long fragment; + + for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES(sblk->fragments); i++) { ++ /* XXX */ + SQUASHFS_SWAP_FRAGMENT_INDEXES((&fragment), + &msblk->fragment_index[i], 1); + msblk->fragment_index[i] = fragment; @@ -1153,6 +1098,34 @@ +} + + ++static int readahead_metadata(struct super_block *s) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ int i; ++ ++ squashfs_cached_blks = SQUASHFS_CACHED_BLKS; ++ ++ /* Init inode_table block pointer array */ ++ msblk->block_cache = kmalloc(sizeof(struct squashfs_cache) * ++ squashfs_cached_blks, GFP_KERNEL); ++ if (msblk->block_cache == NULL) { ++ ERROR("Failed to allocate block cache\n"); ++ goto failed; ++ } ++ ++ for (i = 0; i < squashfs_cached_blks; i++) ++ msblk->block_cache[i].block = SQUASHFS_INVALID_BLK; ++ ++ msblk->next_cache = 0; ++ msblk->unused_cache_blks = squashfs_cached_blks; ++ ++ return 1; ++ ++failed: ++ return 0; ++} ++ ++ +static int supported_squashfs_filesystem(struct squashfs_sb_info *msblk, int silent) +{ + struct squashfs_super_block *sblk = &msblk->sblk; @@ -1165,16 +1138,14 @@ + if (!squashfs_1_0_supported(msblk)) { + SERROR("Major/Minor mismatch, Squashfs 1.0 filesystems " + "are unsupported\n"); -+ SERROR("Please recompile with " -+ "Squashfs 1.0 support enabled\n"); ++ SERROR("Please recompile with Squashfs 1.0 support enabled\n"); + return 0; + } + } else if (sblk->s_major == 2) { + if (!squashfs_2_0_supported(msblk)) { + SERROR("Major/Minor mismatch, Squashfs 2.0 filesystems " + "are unsupported\n"); -+ SERROR("Please recompile with " -+ "Squashfs 2.0 support enabled\n"); ++ SERROR("Please recompile with Squashfs 2.0 support enabled\n"); + return 0; + } + } else if(sblk->s_major != SQUASHFS_MAJOR || sblk->s_minor > @@ -1197,16 +1168,17 @@ + char b[BDEVNAME_SIZE]; + struct inode *root; + -+ TRACE("Entered squashfs_read_superblock\n"); ++ TRACE("Entered squashfs_fill_superblock\n"); + -+ if (!(s->s_fs_info = kmalloc(sizeof(struct squashfs_sb_info), -+ GFP_KERNEL))) { ++ s->s_fs_info = kzalloc(sizeof(struct squashfs_sb_info), GFP_KERNEL); ++ if (s->s_fs_info == NULL) { + ERROR("Failed to allocate superblock\n"); + goto failure; + } -+ memset(s->s_fs_info, 0, sizeof(struct squashfs_sb_info)); + msblk = s->s_fs_info; -+ if (!(msblk->stream.workspace = vmalloc(zlib_inflate_workspacesize()))) { ++ ++ msblk->stream.workspace = vmalloc(zlib_inflate_workspacesize()); ++ if (msblk->stream.workspace == NULL) { + ERROR("Failed to allocate zlib workspace\n"); + goto failure; + } @@ -1224,6 +1196,9 @@ + init_waitqueue_head(&msblk->waitq); + init_waitqueue_head(&msblk->fragment_wait_queue); + ++ /* sblk->bytes_used is checked in squashfs_read_data to ensure reads are not ++ * beyond filesystem end. As we're using squashfs_read_data to read sblk here, ++ * first set sblk->bytes_used to a useful value */ + sblk->bytes_used = sizeof(struct squashfs_super_block); + if (!squashfs_read_data(s, (char *) sblk, SQUASHFS_START, + sizeof(struct squashfs_super_block) | @@ -1233,13 +1208,12 @@ + } + + /* Check it is a SQUASHFS superblock */ -+ msblk->swap = 0; + if ((s->s_magic = sblk->s_magic) != SQUASHFS_MAGIC) { + if (sblk->s_magic == SQUASHFS_MAGIC_SWAP) { + struct squashfs_super_block ssblk; + -+ WARNING("Mounting a different endian SQUASHFS " -+ "filesystem on %s\n", bdevname(s->s_bdev, b)); ++ WARNING("Mounting a different endian SQUASHFS filesystem on %s\n", ++ bdevname(s->s_bdev, b)); + + SQUASHFS_SWAP_SUPER_BLOCK(&ssblk, sblk); + memcpy(sblk, &ssblk, sizeof(struct squashfs_super_block)); @@ -1265,15 +1239,12 @@ + goto failed_mount; + + TRACE("Found valid superblock on %s\n", bdevname(s->s_bdev, b)); -+ TRACE("Inodes are %scompressed\n", -+ SQUASHFS_UNCOMPRESSED_INODES -+ (sblk->flags) ? "un" : ""); -+ TRACE("Data is %scompressed\n", -+ SQUASHFS_UNCOMPRESSED_DATA(sblk->flags) ++ TRACE("Inodes are %scompressed\n", SQUASHFS_UNCOMPRESSED_INODES(sblk->flags) ++ ? "un" : ""); ++ TRACE("Data is %scompressed\n", SQUASHFS_UNCOMPRESSED_DATA(sblk->flags) + ? "un" : ""); -+ TRACE("Check data is %s present in the filesystem\n", -+ SQUASHFS_CHECK_DATA(sblk->flags) ? -+ "" : "not"); ++ TRACE("Check data is %spresent in the filesystem\n", ++ SQUASHFS_CHECK_DATA(sblk->flags) ? "" : "not "); + TRACE("Filesystem size %lld bytes\n", sblk->bytes_used); + TRACE("Block size %d\n", sblk->block_size); + TRACE("Number of inodes %d\n", sblk->inodes); @@ -1284,34 +1255,27 @@ + TRACE("sblk->inode_table_start %llx\n", sblk->inode_table_start); + TRACE("sblk->directory_table_start %llx\n", sblk->directory_table_start); + if (sblk->s_major > 1) -+ TRACE("sblk->fragment_table_start %llx\n", -+ sblk->fragment_table_start); ++ TRACE("sblk->fragment_table_start %llx\n", sblk->fragment_table_start); + TRACE("sblk->uid_start %llx\n", sblk->uid_start); + ++ s->s_maxbytes = MAX_LFS_FILESIZE; + s->s_flags |= MS_RDONLY; + s->s_op = &squashfs_super_ops; + -+ /* Init inode_table block pointer array */ -+ if (!(msblk->block_cache = kmalloc(sizeof(struct squashfs_cache) * -+ SQUASHFS_CACHED_BLKS, GFP_KERNEL))) { -+ ERROR("Failed to allocate block cache\n"); ++ if (readahead_metadata(s) == 0) + goto failed_mount; -+ } -+ -+ for (i = 0; i < SQUASHFS_CACHED_BLKS; i++) -+ msblk->block_cache[i].block = SQUASHFS_INVALID_BLK; -+ -+ msblk->next_cache = 0; + + /* Allocate read_page block */ -+ if (!(msblk->read_page = kmalloc(sblk->block_size, GFP_KERNEL))) { ++ msblk->read_page = vmalloc(sblk->block_size); ++ if (msblk->read_page == NULL) { + ERROR("Failed to allocate read_page block\n"); + goto failed_mount; + } + + /* Allocate uid and gid tables */ -+ if (!(msblk->uid = kmalloc((sblk->no_uids + sblk->no_guids) * -+ sizeof(unsigned int), GFP_KERNEL))) { ++ msblk->uid = kmalloc((sblk->no_uids + sblk->no_guids) * ++ sizeof(unsigned int), GFP_KERNEL); ++ if (msblk->uid == NULL) { + ERROR("Failed to allocate uid/gid table\n"); + goto failed_mount; + } @@ -1343,19 +1307,19 @@ + if (sblk->s_major == 1 && squashfs_1_0_supported(msblk)) + goto allocate_root; + -+ if (!(msblk->fragment = kmalloc(sizeof(struct squashfs_fragment_cache) * -+ SQUASHFS_CACHED_FRAGMENTS, GFP_KERNEL))) { ++ msblk->fragment = kzalloc(sizeof(struct squashfs_fragment_cache) * ++ SQUASHFS_CACHED_FRAGMENTS, GFP_KERNEL); ++ if (msblk->fragment == NULL) { + ERROR("Failed to allocate fragment block cache\n"); + goto failed_mount; + } + + for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++) { -+ msblk->fragment[i].locked = 0; + msblk->fragment[i].block = SQUASHFS_INVALID_BLK; -+ msblk->fragment[i].data = NULL; + } + + msblk->next_fragment = 0; ++ msblk->unused_frag_blks = SQUASHFS_CACHED_FRAGMENTS; + + /* Allocate and read fragment index table */ + if (msblk->read_fragment_index_table(s) == 0) @@ -1377,13 +1341,14 @@ + goto failed_mount; + insert_inode_hash(root); + -+ if ((s->s_root = d_alloc_root(root)) == NULL) { ++ s->s_root = d_alloc_root(root); ++ if (s->s_root == NULL) { + ERROR("Root inode create failed\n"); + iput(root); + goto failed_mount; + } + -+ TRACE("Leaving squashfs_read_super\n"); ++ TRACE("Leaving squashfs_fill_super\n"); + return 0; + +failed_mount: @@ -1391,7 +1356,7 @@ + kfree(msblk->fragment_index); + kfree(msblk->fragment); + kfree(msblk->uid); -+ kfree(msblk->read_page); ++ vfree(msblk->read_page); + kfree(msblk->block_cache); + kfree(msblk->fragment_index_2); + vfree(msblk->stream.workspace); @@ -1426,7 +1391,7 @@ +static int squashfs_symlink_readpage(struct file *file, struct page *page) +{ + struct inode *inode = page->mapping->host; -+ int index = page->index << PAGE_CACHE_SHIFT, length, bytes; ++ int index = page->index << PAGE_CACHE_SHIFT, length, bytes, avail_bytes; + long long block = SQUASHFS_I(inode)->start_block; + int offset = SQUASHFS_I(inode)->offset; + void *pageaddr = kmap(page); @@ -1437,11 +1402,10 @@ + SQUASHFS_I(inode)->offset); + + for (length = 0; length < index; length += bytes) { -+ if (!(bytes = squashfs_get_cached_block(inode->i_sb, NULL, -+ block, offset, PAGE_CACHE_SIZE, &block, -+ &offset))) { -+ ERROR("Unable to read symbolic link [%llx:%x]\n", block, -+ offset); ++ bytes = squashfs_get_cached_block(inode->i_sb, NULL, block, ++ offset, PAGE_CACHE_SIZE, &block, &offset); ++ if (bytes == 0) { ++ ERROR("Unable to read symbolic link [%llx:%x]\n", block, offset); + goto skip_read; + } + } @@ -1452,11 +1416,11 @@ + goto skip_read; + } + -+ bytes = (i_size_read(inode) - length) > PAGE_CACHE_SIZE ? PAGE_CACHE_SIZE : -+ i_size_read(inode) - length; ++ avail_bytes = min_t(int, i_size_read(inode) - length, PAGE_CACHE_SIZE); + -+ if (!(bytes = squashfs_get_cached_block(inode->i_sb, pageaddr, block, -+ offset, bytes, &block, &offset))) ++ bytes = squashfs_get_cached_block(inode->i_sb, pageaddr, block, offset, ++ avail_bytes, &block, &offset); ++ if (bytes == 0) + ERROR("Unable to read symbolic link [%llx:%x]\n", block, offset); + +skip_read: @@ -1480,10 +1444,10 @@ + + TRACE("locate_meta_index: index %d, offset %d\n", index, offset); + -+ if(msblk->meta_index == NULL) ++ if (msblk->meta_index == NULL) + goto not_allocated; + -+ for (i = 0; i < SQUASHFS_META_NUMBER; i ++) ++ for (i = 0; i < SQUASHFS_META_NUMBER; i ++) { + if (msblk->meta_index[i].inode_number == inode->i_ino && + msblk->meta_index[i].offset >= offset && + msblk->meta_index[i].offset <= index && @@ -1493,6 +1457,7 @@ + meta = &msblk->meta_index[i]; + offset = meta->offset; + } ++ } + + if (meta) + meta->locked = 1; @@ -1514,25 +1479,26 @@ + + TRACE("empty_meta_index: offset %d, skip %d\n", offset, skip); + -+ if(msblk->meta_index == NULL) { -+ if (!(msblk->meta_index = kmalloc(sizeof(struct meta_index) * -+ SQUASHFS_META_NUMBER, GFP_KERNEL))) { ++ if (msblk->meta_index == NULL) { ++ msblk->meta_index = kmalloc(sizeof(struct meta_index) * ++ SQUASHFS_META_NUMBER, GFP_KERNEL); ++ if (msblk->meta_index == NULL) { + ERROR("Failed to allocate meta_index\n"); + goto failed; + } -+ for(i = 0; i < SQUASHFS_META_NUMBER; i++) { ++ for (i = 0; i < SQUASHFS_META_NUMBER; i++) { + msblk->meta_index[i].inode_number = 0; + msblk->meta_index[i].locked = 0; + } + msblk->next_meta_index = 0; + } + -+ for(i = SQUASHFS_META_NUMBER; i && ++ for (i = SQUASHFS_META_NUMBER; i && + msblk->meta_index[msblk->next_meta_index].locked; i --) + msblk->next_meta_index = (msblk->next_meta_index + 1) % + SQUASHFS_META_NUMBER; + -+ if(i == 0) { ++ if (i == 0) { + TRACE("empty_meta_index: failed!\n"); + goto failed; + } @@ -1565,7 +1531,7 @@ + + +static int read_block_index(struct super_block *s, int blocks, char *block_list, -+ long long *start_block, int *offset) ++ long long *start_block, int *offset) +{ + struct squashfs_sb_info *msblk = s->s_fs_info; + unsigned int *block_listp; @@ -1576,19 +1542,18 @@ + + if (!squashfs_get_cached_block(s, sblock_list, *start_block, + *offset, blocks << 2, start_block, offset)) { -+ ERROR("Unable to read block list [%llx:%x]\n", -+ *start_block, *offset); ++ ERROR("Fail reading block list [%llx:%x]\n", *start_block, *offset); + goto failure; + } + SQUASHFS_SWAP_INTS(((unsigned int *)block_list), + ((unsigned int *)sblock_list), blocks); -+ } else ++ } else { + if (!squashfs_get_cached_block(s, block_list, *start_block, + *offset, blocks << 2, start_block, offset)) { -+ ERROR("Unable to read block list [%llx:%x]\n", -+ *start_block, *offset); ++ ERROR("Fail reading block list [%llx:%x]\n", *start_block, *offset); + goto failure; + } ++ } + + for (block_listp = (unsigned int *) block_list; blocks; + block_listp++, blocks --) @@ -1626,25 +1591,26 @@ + + index /= SQUASHFS_META_INDEXES * skip; + -+ while ( offset < index ) { ++ while (offset < index) { + meta = locate_meta_index(inode, index, offset + 1); + + if (meta == NULL) { -+ if ((meta = empty_meta_index(inode, offset + 1, -+ skip)) == NULL) ++ meta = empty_meta_index(inode, offset + 1, skip); ++ if (meta == NULL) + goto all_done; + } else { + if(meta->entries == 0) + goto failed; ++ /* XXX */ + offset = index < meta->offset + meta->entries ? index : + meta->offset + meta->entries - 1; ++ /* XXX */ + meta_entry = &meta->meta_entry[offset - meta->offset]; + cur_index_block = meta_entry->index_block + sblk->inode_table_start; + cur_offset = meta_entry->offset; + cur_data_block = meta_entry->data_block; + TRACE("get_meta_index: offset %d, meta->offset %d, " -+ "meta->entries %d\n", offset, meta->offset, -+ meta->entries); ++ "meta->entries %d\n", offset, meta->offset, meta->entries); + TRACE("get_meta_index: index_block 0x%llx, offset 0x%x" + " data_block 0x%llx\n", cur_index_block, + cur_offset, cur_data_block); @@ -1655,11 +1621,9 @@ + int blocks = skip * SQUASHFS_META_INDEXES; + + while (blocks) { -+ int block = blocks > (SIZE >> 2) ? (SIZE >> 2) : -+ blocks; -+ int res = read_block_index(inode->i_sb, block, -+ block_list, &cur_index_block, -+ &cur_offset); ++ int block = blocks > (SIZE >> 2) ? (SIZE >> 2) : blocks; ++ int res = read_block_index(inode->i_sb, block, block_list, ++ &cur_index_block, &cur_offset); + + if (res == -1) + goto failed; @@ -1706,15 +1670,14 @@ + block_list); + + TRACE("read_blocklist: res %d, index %d, block_ptr 0x%llx, offset" -+ " 0x%x, block 0x%llx\n", res, index, block_ptr, offset, -+ block); ++ " 0x%x, block 0x%llx\n", res, index, block_ptr, offset, block); + + if(res == -1) + goto failure; + + index -= res; + -+ while ( index ) { ++ while (index) { + int blocks = index > (SIZE >> 2) ? (SIZE >> 2) : index; + int res = read_block_index(inode->i_sb, blocks, block_list, + &block_ptr, &offset); @@ -1724,8 +1687,7 @@ + index -= blocks; + } + -+ if (read_block_index(inode->i_sb, 1, block_list, -+ &block_ptr, &offset) == -1) ++ if (read_block_index(inode->i_sb, 1, block_list, &block_ptr, &offset) == -1) + goto failure; + *bsize = *((unsigned int *) block_list); + @@ -1741,9 +1703,10 @@ + struct inode *inode = page->mapping->host; + struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; + struct squashfs_super_block *sblk = &msblk->sblk; -+ unsigned char *block_list; ++ unsigned char *block_list = NULL; + long long block; -+ unsigned int bsize, i = 0, bytes = 0, byte_offset = 0; ++ unsigned int bsize, i; ++ int bytes; + int index = page->index >> (sblk->block_log - PAGE_CACHE_SHIFT); + void *pageaddr; + struct squashfs_fragment_cache *fragment = NULL; @@ -1752,64 +1715,65 @@ + int mask = (1 << (sblk->block_log - PAGE_CACHE_SHIFT)) - 1; + int start_index = page->index & ~mask; + int end_index = start_index | mask; ++ int file_end = i_size_read(inode) >> sblk->block_log; ++ int sparse = 0; + + TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n", -+ page->index, -+ SQUASHFS_I(inode)->start_block); -+ -+ if (!(block_list = kmalloc(SIZE, GFP_KERNEL))) { -+ ERROR("Failed to allocate block_list\n"); -+ goto skip_read; -+ } ++ page->index, SQUASHFS_I(inode)->start_block); + + if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> + PAGE_CACHE_SHIFT)) -+ goto skip_read; ++ goto out; + + if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK -+ || index < (i_size_read(inode) >> -+ sblk->block_log)) { -+ if ((block = (msblk->read_blocklist)(inode, index, 1, -+ block_list, NULL, &bsize)) == 0) -+ goto skip_read; ++ || index < file_end) { ++ block_list = kmalloc(SIZE, GFP_KERNEL); ++ if (block_list == NULL) { ++ ERROR("Failed to allocate block_list\n"); ++ goto error_out; ++ } ++ ++ block = (msblk->read_blocklist)(inode, index, 1, block_list, NULL, &bsize); ++ if (block == 0) ++ goto error_out; + -+ mutex_lock(&msblk->read_page_mutex); ++ if (bsize == 0) { /* hole */ ++ bytes = index == file_end ? ++ (i_size_read(inode) & (sblk->block_size - 1)) : sblk->block_size; ++ sparse = 1; ++ } else { ++ mutex_lock(&msblk->read_page_mutex); + -+ if (!(bytes = squashfs_read_data(inode->i_sb, msblk->read_page, -+ block, bsize, NULL, sblk->block_size))) { -+ ERROR("Unable to read page, block %llx, size %x\n", block, -+ bsize); -+ mutex_unlock(&msblk->read_page_mutex); -+ goto skip_read; ++ bytes = squashfs_read_data(inode->i_sb, msblk->read_page, block, ++ bsize, NULL, sblk->block_size); ++ ++ if (bytes == 0) { ++ ERROR("Unable to read page, block %llx, size %x\n", block, bsize); ++ mutex_unlock(&msblk->read_page_mutex); ++ goto error_out; ++ } + } + } else { -+ if ((fragment = get_cached_fragment(inode->i_sb, -+ SQUASHFS_I(inode)-> -+ u.s1.fragment_start_block, -+ SQUASHFS_I(inode)->u.s1.fragment_size)) -+ == NULL) { ++ fragment = get_cached_fragment(inode->i_sb, ++ SQUASHFS_I(inode)-> u.s1.fragment_start_block, ++ SQUASHFS_I(inode)->u.s1.fragment_size); ++ ++ if (fragment == NULL) { + ERROR("Unable to read page, block %llx, size %x\n", -+ SQUASHFS_I(inode)-> -+ u.s1.fragment_start_block, -+ (int) SQUASHFS_I(inode)-> -+ u.s1.fragment_size); -+ goto skip_read; ++ SQUASHFS_I(inode)->u.s1.fragment_start_block, ++ (int) SQUASHFS_I(inode)->u.s1.fragment_size); ++ goto error_out; + } -+ bytes = SQUASHFS_I(inode)->u.s1.fragment_offset + -+ (i_size_read(inode) & (sblk->block_size -+ - 1)); -+ byte_offset = SQUASHFS_I(inode)->u.s1.fragment_offset; -+ data_ptr = fragment->data; ++ bytes = i_size_read(inode) & (sblk->block_size - 1); ++ data_ptr = fragment->data + SQUASHFS_I(inode)->u.s1.fragment_offset; + } + -+ for (i = start_index; i <= end_index && byte_offset < bytes; -+ i++, byte_offset += PAGE_CACHE_SIZE) { ++ for (i = start_index; i <= end_index && bytes > 0; i++, ++ bytes -= PAGE_CACHE_SIZE, data_ptr += PAGE_CACHE_SIZE) { + struct page *push_page; -+ int avail = (bytes - byte_offset) > PAGE_CACHE_SIZE ? -+ PAGE_CACHE_SIZE : bytes - byte_offset; ++ int avail = sparse ? 0 : min_t(unsigned int, bytes, PAGE_CACHE_SIZE); + -+ TRACE("bytes %d, i %d, byte_offset %d, available_bytes %d\n", -+ bytes, i, byte_offset, avail); ++ TRACE("bytes %d, i %d, available_bytes %d\n", bytes, i, avail); + + push_page = (i == page->index) ? page : + grab_cache_page_nowait(page->mapping, i); @@ -1821,7 +1785,7 @@ + goto skip_page; + + pageaddr = kmap_atomic(push_page, KM_USER0); -+ memcpy(pageaddr, data_ptr + byte_offset, avail); ++ memcpy(pageaddr, data_ptr, avail); + memset(pageaddr + avail, 0, PAGE_CACHE_SIZE - avail); + kunmap_atomic(pageaddr, KM_USER0); + flush_dcache_page(push_page); @@ -1833,98 +1797,24 @@ + } + + if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK -+ || index < (i_size_read(inode) >> -+ sblk->block_log)) -+ mutex_unlock(&msblk->read_page_mutex); -+ else ++ || index < file_end) { ++ if (!sparse) ++ mutex_unlock(&msblk->read_page_mutex); ++ kfree(block_list); ++ } else + release_cached_fragment(msblk, fragment); + -+ kfree(block_list); -+ return 0; -+ -+skip_read: -+ pageaddr = kmap_atomic(page, KM_USER0); -+ memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes); -+ kunmap_atomic(pageaddr, KM_USER0); -+ flush_dcache_page(page); -+ SetPageUptodate(page); -+ unlock_page(page); -+ -+ kfree(block_list); + return 0; -+} -+ + -+static int squashfs_readpage4K(struct file *file, struct page *page) -+{ -+ struct inode *inode = page->mapping->host; -+ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; -+ struct squashfs_super_block *sblk = &msblk->sblk; -+ unsigned char *block_list; -+ long long block; -+ unsigned int bsize, bytes = 0; -+ void *pageaddr; -+ -+ TRACE("Entered squashfs_readpage4K, page index %lx, start block %llx\n", -+ page->index, -+ SQUASHFS_I(inode)->start_block); -+ -+ if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> -+ PAGE_CACHE_SHIFT)) { -+ block_list = NULL; -+ goto skip_read; -+ } -+ -+ if (!(block_list = kmalloc(SIZE, GFP_KERNEL))) { -+ ERROR("Failed to allocate block_list\n"); -+ goto skip_read; -+ } -+ -+ if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK -+ || page->index < (i_size_read(inode) >> -+ sblk->block_log)) { -+ block = (msblk->read_blocklist)(inode, page->index, 1, -+ block_list, NULL, &bsize); -+ if(block == 0) -+ goto skip_read; -+ -+ mutex_lock(&msblk->read_page_mutex); -+ bytes = squashfs_read_data(inode->i_sb, msblk->read_page, block, -+ bsize, NULL, sblk->block_size); -+ if (bytes) { -+ pageaddr = kmap_atomic(page, KM_USER0); -+ memcpy(pageaddr, msblk->read_page, bytes); -+ kunmap_atomic(pageaddr, KM_USER0); -+ } else -+ ERROR("Unable to read page, block %llx, size %x\n", -+ block, bsize); -+ mutex_unlock(&msblk->read_page_mutex); -+ } else { -+ struct squashfs_fragment_cache *fragment = -+ get_cached_fragment(inode->i_sb, -+ SQUASHFS_I(inode)-> -+ u.s1.fragment_start_block, -+ SQUASHFS_I(inode)-> u.s1.fragment_size); -+ if (fragment) { -+ bytes = i_size_read(inode) & (sblk->block_size - 1); -+ pageaddr = kmap_atomic(page, KM_USER0); -+ memcpy(pageaddr, fragment->data + SQUASHFS_I(inode)-> -+ u.s1.fragment_offset, bytes); -+ kunmap_atomic(pageaddr, KM_USER0); -+ release_cached_fragment(msblk, fragment); -+ } else -+ ERROR("Unable to read page, block %llx, size %x\n", -+ SQUASHFS_I(inode)-> -+ u.s1.fragment_start_block, (int) -+ SQUASHFS_I(inode)-> u.s1.fragment_size); -+ } -+ -+skip_read: ++error_out: ++ SetPageError(page); ++out: + pageaddr = kmap_atomic(page, KM_USER0); -+ memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes); ++ memset(pageaddr, 0, PAGE_CACHE_SIZE); + kunmap_atomic(pageaddr, KM_USER0); + flush_dcache_page(page); -+ SetPageUptodate(page); ++ if (!PageError(page)) ++ SetPageUptodate(page); + unlock_page(page); + + kfree(block_list); @@ -1932,10 +1822,9 @@ +} + + -+static int get_dir_index_using_offset(struct super_block *s, long long -+ *next_block, unsigned int *next_offset, -+ long long index_start, -+ unsigned int index_offset, int i_count, ++static int get_dir_index_using_offset(struct super_block *s, ++ long long *next_block, unsigned int *next_offset, ++ long long index_start, unsigned int index_offset, int i_count, + long long f_pos) +{ + struct squashfs_sb_info *msblk = s->s_fs_info; @@ -1953,23 +1842,18 @@ + for (i = 0; i < i_count; i++) { + if (msblk->swap) { + struct squashfs_dir_index sindex; -+ squashfs_get_cached_block(s, (char *) &sindex, -+ index_start, index_offset, -+ sizeof(sindex), &index_start, -+ &index_offset); ++ squashfs_get_cached_block(s, &sindex, index_start, index_offset, ++ sizeof(sindex), &index_start, &index_offset); + SQUASHFS_SWAP_DIR_INDEX(&index, &sindex); + } else -+ squashfs_get_cached_block(s, (char *) &index, -+ index_start, index_offset, -+ sizeof(index), &index_start, -+ &index_offset); ++ squashfs_get_cached_block(s, &index, index_start, index_offset, ++ sizeof(index), &index_start, &index_offset); + + if (index.index > f_pos) + break; + + squashfs_get_cached_block(s, NULL, index_start, index_offset, -+ index.size + 1, &index_start, -+ &index_offset); ++ index.size + 1, &index_start, &index_offset); + + length = index.index; + *next_block = index.start_block + sblk->directory_table_start; @@ -1982,10 +1866,9 @@ +} + + -+static int get_dir_index_using_name(struct super_block *s, long long -+ *next_block, unsigned int *next_offset, -+ long long index_start, -+ unsigned int index_offset, int i_count, ++static int get_dir_index_using_name(struct super_block *s, ++ long long *next_block, unsigned int *next_offset, ++ long long index_start, unsigned int index_offset, int i_count, + const char *name, int size) +{ + struct squashfs_sb_info *msblk = s->s_fs_info; @@ -1996,8 +1879,9 @@ + + TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count); + -+ if (!(str = kmalloc(sizeof(struct squashfs_dir_index) + -+ (SQUASHFS_NAME_LEN + 1) * 2, GFP_KERNEL))) { ++ str = kmalloc(sizeof(struct squashfs_dir_index) + ++ (SQUASHFS_NAME_LEN + 1) * 2, GFP_KERNEL); ++ if (str == NULL) { + ERROR("Failed to allocate squashfs_dir_index\n"); + goto failure; + } @@ -2009,20 +1893,15 @@ + for (i = 0; i < i_count; i++) { + if (msblk->swap) { + struct squashfs_dir_index sindex; -+ squashfs_get_cached_block(s, (char *) &sindex, -+ index_start, index_offset, -+ sizeof(sindex), &index_start, -+ &index_offset); ++ squashfs_get_cached_block(s, &sindex, index_start, index_offset, ++ sizeof(sindex), &index_start, &index_offset); + SQUASHFS_SWAP_DIR_INDEX(index, &sindex); + } else -+ squashfs_get_cached_block(s, (char *) index, -+ index_start, index_offset, -+ sizeof(struct squashfs_dir_index), -+ &index_start, &index_offset); ++ squashfs_get_cached_block(s, index, index_start, index_offset, ++ sizeof(struct squashfs_dir_index), &index_start, &index_offset); + -+ squashfs_get_cached_block(s, index->name, index_start, -+ index_offset, index->size + 1, -+ &index_start, &index_offset); ++ squashfs_get_cached_block(s, index->name, index_start, index_offset, ++ index->size + 1, &index_start, &index_offset); + + index->name[index->size + 1] = '\0'; + @@ -2035,6 +1914,7 @@ + + *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE; + kfree(str); ++ +failure: + return length + 3; +} @@ -2047,15 +1927,15 @@ + struct squashfs_super_block *sblk = &msblk->sblk; + long long next_block = SQUASHFS_I(i)->start_block + + sblk->directory_table_start; -+ int next_offset = SQUASHFS_I(i)->offset, length = 0, -+ dir_count; ++ int next_offset = SQUASHFS_I(i)->offset, length = 0, dir_count; + struct squashfs_dir_header dirh; + struct squashfs_dir_entry *dire; + + TRACE("Entered squashfs_readdir [%llx:%x]\n", next_block, next_offset); + -+ if (!(dire = kmalloc(sizeof(struct squashfs_dir_entry) + -+ SQUASHFS_NAME_LEN + 1, GFP_KERNEL))) { ++ dire = kmalloc(sizeof(struct squashfs_dir_entry) + ++ SQUASHFS_NAME_LEN + 1, GFP_KERNEL); ++ if (dire == NULL) { + ERROR("Failed to allocate squashfs_dir_entry\n"); + goto finish; + } @@ -2075,14 +1955,12 @@ + } + TRACE("Calling filldir(%x, %s, %d, %d, %d, %d)\n", + (unsigned int) dirent, name, size, (int) -+ file->f_pos, i_ino, -+ squashfs_filetype_table[1]); ++ file->f_pos, i_ino, squashfs_filetype_table[1]); + -+ if (filldir(dirent, name, size, -+ file->f_pos, i_ino, ++ if (filldir(dirent, name, size, file->f_pos, i_ino, + squashfs_filetype_table[1]) < 0) { + TRACE("Filldir returned less than 0\n"); -+ goto finish; ++ goto finish; + } + file->f_pos += size; + } @@ -2090,25 +1968,22 @@ + length = get_dir_index_using_offset(i->i_sb, &next_block, &next_offset, + SQUASHFS_I(i)->u.s2.directory_index_start, + SQUASHFS_I(i)->u.s2.directory_index_offset, -+ SQUASHFS_I(i)->u.s2.directory_index_count, -+ file->f_pos); ++ SQUASHFS_I(i)->u.s2.directory_index_count, file->f_pos); + + while (length < i_size_read(i)) { + /* read directory header */ + if (msblk->swap) { + struct squashfs_dir_header sdirh; + -+ if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh, -+ next_block, next_offset, sizeof(sdirh), -+ &next_block, &next_offset)) ++ if (!squashfs_get_cached_block(i->i_sb, &sdirh, next_block, ++ next_offset, sizeof(sdirh), &next_block, &next_offset)) + goto failed_read; + + length += sizeof(sdirh); + SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh); + } else { -+ if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh, -+ next_block, next_offset, sizeof(dirh), -+ &next_block, &next_offset)) ++ if (!squashfs_get_cached_block(i->i_sb, &dirh, next_block, ++ next_offset, sizeof(dirh), &next_block, &next_offset)) + goto failed_read; + + length += sizeof(dirh); @@ -2118,28 +1993,22 @@ + while (dir_count--) { + if (msblk->swap) { + struct squashfs_dir_entry sdire; -+ if (!squashfs_get_cached_block(i->i_sb, (char *) -+ &sdire, next_block, next_offset, -+ sizeof(sdire), &next_block, -+ &next_offset)) ++ if (!squashfs_get_cached_block(i->i_sb, &sdire, next_block, ++ next_offset, sizeof(sdire), &next_block, &next_offset)) + goto failed_read; + + length += sizeof(sdire); + SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire); + } else { -+ if (!squashfs_get_cached_block(i->i_sb, (char *) -+ dire, next_block, next_offset, -+ sizeof(*dire), &next_block, -+ &next_offset)) ++ if (!squashfs_get_cached_block(i->i_sb, dire, next_block, ++ next_offset, sizeof(*dire), &next_block, &next_offset)) + goto failed_read; + + length += sizeof(*dire); + } + -+ if (!squashfs_get_cached_block(i->i_sb, dire->name, -+ next_block, next_offset, -+ dire->size + 1, &next_block, -+ &next_offset)) ++ if (!squashfs_get_cached_block(i->i_sb, dire->name, next_block, ++ next_offset, dire->size + 1, &next_block, &next_offset)) + goto failed_read; + + length += dire->size + 1; @@ -2150,17 +2019,14 @@ + dire->name[dire->size + 1] = '\0'; + + TRACE("Calling filldir(%x, %s, %d, %d, %x:%x, %d, %d)\n", -+ (unsigned int) dirent, dire->name, -+ dire->size + 1, (int) file->f_pos, -+ dirh.start_block, dire->offset, ++ (unsigned int) dirent, dire->name, dire->size + 1, ++ (int) file->f_pos, dirh.start_block, dire->offset, + dirh.inode_number + dire->inode_number, + squashfs_filetype_table[dire->type]); + -+ if (filldir(dirent, dire->name, dire->size + 1, -+ file->f_pos, ++ if (filldir(dirent, dire->name, dire->size + 1, file->f_pos, + dirh.inode_number + dire->inode_number, -+ squashfs_filetype_table[dire->type]) -+ < 0) { ++ squashfs_filetype_table[dire->type]) < 0) { + TRACE("Filldir returned less than 0\n"); + goto finish; + } @@ -2190,15 +2056,15 @@ + struct squashfs_super_block *sblk = &msblk->sblk; + long long next_block = SQUASHFS_I(i)->start_block + + sblk->directory_table_start; -+ int next_offset = SQUASHFS_I(i)->offset, length = 0, -+ dir_count; ++ int next_offset = SQUASHFS_I(i)->offset, length = 0, dir_count; + struct squashfs_dir_header dirh; + struct squashfs_dir_entry *dire; + + TRACE("Entered squashfs_lookup [%llx:%x]\n", next_block, next_offset); + -+ if (!(dire = kmalloc(sizeof(struct squashfs_dir_entry) + -+ SQUASHFS_NAME_LEN + 1, GFP_KERNEL))) { ++ dire = kmalloc(sizeof(struct squashfs_dir_entry) + ++ SQUASHFS_NAME_LEN + 1, GFP_KERNEL); ++ if (dire == NULL) { + ERROR("Failed to allocate squashfs_dir_entry\n"); + goto exit_lookup; + } @@ -2209,24 +2075,21 @@ + length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset, + SQUASHFS_I(i)->u.s2.directory_index_start, + SQUASHFS_I(i)->u.s2.directory_index_offset, -+ SQUASHFS_I(i)->u.s2.directory_index_count, name, -+ len); ++ SQUASHFS_I(i)->u.s2.directory_index_count, name, len); + + while (length < i_size_read(i)) { + /* read directory header */ + if (msblk->swap) { + struct squashfs_dir_header sdirh; -+ if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh, -+ next_block, next_offset, sizeof(sdirh), -+ &next_block, &next_offset)) ++ if (!squashfs_get_cached_block(i->i_sb, &sdirh, next_block, ++ next_offset, sizeof(sdirh), &next_block, &next_offset)) + goto failed_read; + + length += sizeof(sdirh); + SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh); + } else { -+ if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh, -+ next_block, next_offset, sizeof(dirh), -+ &next_block, &next_offset)) ++ if (!squashfs_get_cached_block(i->i_sb, &dirh, next_block, ++ next_offset, sizeof(dirh), &next_block, &next_offset)) + goto failed_read; + + length += sizeof(dirh); @@ -2236,27 +2099,22 @@ + while (dir_count--) { + if (msblk->swap) { + struct squashfs_dir_entry sdire; -+ if (!squashfs_get_cached_block(i->i_sb, (char *) -+ &sdire, next_block,next_offset, -+ sizeof(sdire), &next_block, -+ &next_offset)) ++ if (!squashfs_get_cached_block(i->i_sb, &sdire, next_block, ++ next_offset, sizeof(sdire), &next_block, &next_offset)) + goto failed_read; + + length += sizeof(sdire); + SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire); + } else { -+ if (!squashfs_get_cached_block(i->i_sb, (char *) -+ dire, next_block,next_offset, -+ sizeof(*dire), &next_block, -+ &next_offset)) ++ if (!squashfs_get_cached_block(i->i_sb, dire, next_block, ++ next_offset, sizeof(*dire), &next_block, &next_offset)) + goto failed_read; + + length += sizeof(*dire); + } + -+ if (!squashfs_get_cached_block(i->i_sb, dire->name, -+ next_block, next_offset, dire->size + 1, -+ &next_block, &next_offset)) ++ if (!squashfs_get_cached_block(i->i_sb, dire->name, next_block, ++ next_offset, dire->size + 1, &next_block, &next_offset)) + goto failed_read; + + length += dire->size + 1; @@ -2268,9 +2126,8 @@ + squashfs_inode_t ino = SQUASHFS_MKINODE(dirh.start_block, + dire->offset); + -+ TRACE("calling squashfs_iget for directory " -+ "entry %s, inode %x:%x, %d\n", name, -+ dirh.start_block, dire->offset, ++ TRACE("calling squashfs_iget for directory entry %s, inode" ++ " %x:%x, %d\n", name, dirh.start_block, dire->offset, + dirh.inode_number + dire->inode_number); + + inode = squashfs_iget(i->i_sb, ino, dirh.inode_number + dire->inode_number); @@ -2308,16 +2165,15 @@ + if (s->s_fs_info) { + struct squashfs_sb_info *sbi = s->s_fs_info; + if (sbi->block_cache) -+ for (i = 0; i < SQUASHFS_CACHED_BLKS; i++) -+ if (sbi->block_cache[i].block != -+ SQUASHFS_INVALID_BLK) -+ kfree(sbi->block_cache[i].data); ++ for (i = 0; i < squashfs_cached_blks; i++) ++ if (sbi->block_cache[i].block != SQUASHFS_INVALID_BLK) ++ vfree(sbi->block_cache[i].data); + if (sbi->fragment) + for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++) -+ SQUASHFS_FREE(sbi->fragment[i].data); ++ vfree(sbi->fragment[i].data); + kfree(sbi->fragment); + kfree(sbi->block_cache); -+ kfree(sbi->read_page); ++ vfree(sbi->read_page); + kfree(sbi->uid); + kfree(sbi->fragment_index); + kfree(sbi->fragment_index_2); @@ -2330,8 +2186,7 @@ + + +static int squashfs_get_sb(struct file_system_type *fs_type, int flags, -+ const char *dev_name, void *data, -+ struct vfsmount *mnt) ++ const char *dev_name, void *data, struct vfsmount *mnt) +{ + return get_sb_bdev(fs_type, flags, dev_name, data, squashfs_fill_super, + mnt); @@ -2344,10 +2199,11 @@ + if (err) + goto out; + -+ printk(KERN_INFO "squashfs: version 3.2-r2 (2007/01/15) " ++ printk(KERN_INFO "squashfs: version 3.3 (2007/10/31) " + "Phillip Lougher\n"); + -+ if ((err = register_filesystem(&squashfs_fs_type))) ++ err = register_filesystem(&squashfs_fs_type); ++ if (err) + destroy_inodecache(); + +out: @@ -2369,9 +2225,7 @@ +{ + struct squashfs_inode_info *ei; + ei = kmem_cache_alloc(squashfs_inode_cachep, GFP_KERNEL); -+ if (!ei) -+ return NULL; -+ return &ei->vfs_inode; ++ return ei ? &ei->vfs_inode : NULL; +} + + @@ -2381,22 +2235,19 @@ +} + + -+static void init_once(void * foo, struct kmem_cache * cachep, unsigned long flags) ++static void init_once(struct kmem_cache *cachep, void *foo) +{ + struct squashfs_inode_info *ei = foo; + -+ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == -+ SLAB_CTOR_CONSTRUCTOR) -+ inode_init_once(&ei->vfs_inode); ++ inode_init_once(&ei->vfs_inode); +} + + +static int __init init_inodecache(void) +{ + squashfs_inode_cachep = kmem_cache_create("squashfs_inode_cache", -+ sizeof(struct squashfs_inode_info), -+ 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, -+ init_once, NULL); ++ sizeof(struct squashfs_inode_info), 0, ++ SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, init_once); + if (squashfs_inode_cachep == NULL) + return -ENOMEM; + return 0; @@ -2411,11 +2262,12 @@ + +module_init(init_squashfs_fs); +module_exit(exit_squashfs_fs); -+MODULE_DESCRIPTION("squashfs 3.2-r2, a compressed read-only filesystem"); -+MODULE_AUTHOR("Phillip Lougher <phillip@lougher.org.uk>"); ++MODULE_DESCRIPTION("squashfs 3.2-r2-CVS, a compressed read-only filesystem"); ++MODULE_AUTHOR("Phillip Lougher <phillip@lougher.demon.co.uk>"); +MODULE_LICENSE("GPL"); ---- /dev/null -+++ linux-2.6.24-rc1/fs/squashfs/Makefile +diff -x .gitignore -Nurp linux-2.6.24/fs/squashfs/Makefile linux-2.6.24-squashfs3.3/fs/squashfs/Makefile +--- linux-2.6.24/fs/squashfs/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24-squashfs3.3/fs/squashfs/Makefile 2005-11-20 14:31:00.000000000 +0000 @@ -0,0 +1,7 @@ +# +# Makefile for the linux squashfs routines. @@ -2424,14 +2276,15 @@ +obj-$(CONFIG_SQUASHFS) += squashfs.o +squashfs-y += inode.o +squashfs-y += squashfs2_0.o ---- /dev/null -+++ linux-2.6.24-rc1/fs/squashfs/squashfs2_0.c -@@ -0,0 +1,742 @@ +diff -x .gitignore -Nurp linux-2.6.24/fs/squashfs/squashfs2_0.c linux-2.6.24-squashfs3.3/fs/squashfs/squashfs2_0.c +--- linux-2.6.24/fs/squashfs/squashfs2_0.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24-squashfs3.3/fs/squashfs/squashfs2_0.c 2007-10-25 00:43:59.000000000 +0100 +@@ -0,0 +1,740 @@ +/* + * Squashfs - a compressed read only filesystem for Linux + * + * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007 -+ * Phillip Lougher <phillip@lougher.org.uk> ++ * Phillip Lougher <phillip@lougher.demon.co.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License @@ -2577,14 +2430,15 @@ + unsigned int block = SQUASHFS_INODE_BLK(inode) + + sblk->inode_table_start; + unsigned int offset = SQUASHFS_INODE_OFFSET(inode); -+ unsigned int ino = i->i_ino; ++ unsigned int ino = SQUASHFS_MK_VFS_INODE(block - ++ sblk->inode_table_start, offset); + long long next_block; + unsigned int next_offset; + union squashfs_inode_header_2 id, sid; + struct squashfs_base_inode_header_2 *inodeb = &id.base, + *sinodeb = &sid.base; + -+ TRACE("Entered squashfs_iget\n"); ++ TRACE("Entered squashfs_read_inode_2\n"); + + if (msblk->swap) { + if (!squashfs_get_cached_block(s, (char *) sinodeb, block, @@ -2641,10 +2495,7 @@ + SQUASHFS_I(i)->start_block = inodep->start_block; + SQUASHFS_I(i)->u.s1.block_list_start = next_block; + SQUASHFS_I(i)->offset = next_offset; -+ if (sblk->block_size > 4096) -+ i->i_data.a_ops = &squashfs_aops; -+ else -+ i->i_data.a_ops = &squashfs_aops_4K; ++ i->i_data.a_ops = &squashfs_aops; + + TRACE("File inode %x:%x, start_block %x, " + "block_list_start %llx, offset %x\n", @@ -3169,14 +3020,15 @@ + + return 1; +} ---- /dev/null -+++ linux-2.6.24-rc1/fs/squashfs/squashfs.h -@@ -0,0 +1,87 @@ +diff -x .gitignore -Nurp linux-2.6.24/fs/squashfs/squashfs.h linux-2.6.24-squashfs3.3/fs/squashfs/squashfs.h +--- linux-2.6.24/fs/squashfs/squashfs.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24-squashfs3.3/fs/squashfs/squashfs.h 2007-08-19 04:23:16.000000000 +0100 +@@ -0,0 +1,86 @@ +/* + * Squashfs - a compressed read only filesystem for Linux + * + * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007 -+ * Phillip Lougher <phillip@lougher.org.uk> ++ * Phillip Lougher <phillip@lougher.demon.co.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License @@ -3224,7 +3076,7 @@ +extern unsigned int squashfs_read_data(struct super_block *s, char *buffer, + long long index, unsigned int length, + long long *next_index, int srclength); -+extern int squashfs_get_cached_block(struct super_block *s, char *buffer, ++extern int squashfs_get_cached_block(struct super_block *s, void *buffer, + long long block, unsigned int offset, + int length, long long *next_block, + unsigned int *next_offset); @@ -3236,7 +3088,6 @@ +extern struct inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode, unsigned int inode_number); +extern const struct address_space_operations squashfs_symlink_aops; +extern const struct address_space_operations squashfs_aops; -+extern const struct address_space_operations squashfs_aops_4K; +extern struct inode_operations squashfs_dir_inode_ops; +#else +#define SQSH_EXTERN static @@ -3259,9 +3110,10 @@ + return 0; +} +#endif ---- /dev/null -+++ linux-2.6.24-rc1/include/linux/squashfs_fs.h -@@ -0,0 +1,934 @@ +diff -x .gitignore -Nurp linux-2.6.24/include/linux/squashfs_fs.h linux-2.6.24-squashfs3.3/include/linux/squashfs_fs.h +--- linux-2.6.24/include/linux/squashfs_fs.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24-squashfs3.3/include/linux/squashfs_fs.h 2007-11-01 03:50:57.000000000 +0000 +@@ -0,0 +1,935 @@ +#ifndef SQUASHFS_FS +#define SQUASHFS_FS + @@ -3269,7 +3121,7 @@ + * Squashfs + * + * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007 -+ * Phillip Lougher <phillip@lougher.org.uk> ++ * Phillip Lougher <phillip@lougher.demon.co.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License @@ -3292,16 +3144,9 @@ +#define CONFIG_SQUASHFS_2_0_COMPATIBILITY +#endif + -+#ifdef CONFIG_SQUASHFS_VMALLOC -+#define SQUASHFS_ALLOC(a) vmalloc(a) -+#define SQUASHFS_FREE(a) vfree(a) -+#else -+#define SQUASHFS_ALLOC(a) kmalloc(a, GFP_KERNEL) -+#define SQUASHFS_FREE(a) kfree(a) -+#endif +#define SQUASHFS_CACHED_FRAGMENTS CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE +#define SQUASHFS_MAJOR 3 -+#define SQUASHFS_MINOR 0 ++#define SQUASHFS_MINOR 1 +#define SQUASHFS_MAGIC 0x73717368 +#define SQUASHFS_MAGIC_SWAP 0x68737173 +#define SQUASHFS_START 0 @@ -3311,10 +3156,10 @@ +#define SQUASHFS_METADATA_LOG 13 + +/* default size of data blocks */ -+#define SQUASHFS_FILE_SIZE 65536 -+#define SQUASHFS_FILE_LOG 16 ++#define SQUASHFS_FILE_SIZE 131072 ++#define SQUASHFS_FILE_LOG 17 + -+#define SQUASHFS_FILE_MAX_SIZE 65536 ++#define SQUASHFS_FILE_MAX_SIZE 1048576 + +/* Max number of uids and gids */ +#define SQUASHFS_UIDS 256 @@ -3395,9 +3240,8 @@ + +#define SQUASHFS_COMPRESSED_BIT_BLOCK (1 << 24) + -+#define SQUASHFS_COMPRESSED_SIZE_BLOCK(B) (((B) & \ -+ ~SQUASHFS_COMPRESSED_BIT_BLOCK) ? (B) & \ -+ ~SQUASHFS_COMPRESSED_BIT_BLOCK : SQUASHFS_COMPRESSED_BIT_BLOCK) ++#define SQUASHFS_COMPRESSED_SIZE_BLOCK(B) ((B) & \ ++ ~SQUASHFS_COMPRESSED_BIT_BLOCK) + +#define SQUASHFS_COMPRESSED_BLOCK(B) (!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK)) + @@ -3893,6 +3737,15 @@ + unsigned int start_block:24; +} __attribute__ ((packed)); + ++union squashfs_inode_header_1 { ++ struct squashfs_base_inode_header_1 base; ++ struct squashfs_dev_inode_header_1 dev; ++ struct squashfs_symlink_inode_header_1 symlink; ++ struct squashfs_reg_inode_header_1 reg; ++ struct squashfs_dir_inode_header_1 dir; ++ struct squashfs_ipc_inode_header_1 ipc; ++}; ++ +#define SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n) \ + SQUASHFS_MEMSET(s, d, n);\ + SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\ @@ -4196,8 +4049,9 @@ + +#endif +#endif ---- /dev/null -+++ linux-2.6.24-rc1/include/linux/squashfs_fs_i.h +diff -x .gitignore -Nurp linux-2.6.24/include/linux/squashfs_fs_i.h linux-2.6.24-squashfs3.3/include/linux/squashfs_fs_i.h +--- linux-2.6.24/include/linux/squashfs_fs_i.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24-squashfs3.3/include/linux/squashfs_fs_i.h 2007-08-19 04:24:08.000000000 +0100 @@ -0,0 +1,45 @@ +#ifndef SQUASHFS_FS_I +#define SQUASHFS_FS_I @@ -4205,7 +4059,7 @@ + * Squashfs + * + * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007 -+ * Phillip Lougher <phillip@lougher.org.uk> ++ * Phillip Lougher <phillip@lougher.demon.co.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License @@ -4244,16 +4098,17 @@ + struct inode vfs_inode; +}; +#endif ---- /dev/null -+++ linux-2.6.24-rc1/include/linux/squashfs_fs_sb.h -@@ -0,0 +1,74 @@ +diff -x .gitignore -Nurp linux-2.6.24/include/linux/squashfs_fs_sb.h linux-2.6.24-squashfs3.3/include/linux/squashfs_fs_sb.h +--- linux-2.6.24/include/linux/squashfs_fs_sb.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24-squashfs3.3/include/linux/squashfs_fs_sb.h 2007-08-19 04:24:26.000000000 +0100 +@@ -0,0 +1,76 @@ +#ifndef SQUASHFS_FS_SB +#define SQUASHFS_FS_SB +/* + * Squashfs + * + * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007 -+ * Phillip Lougher <phillip@lougher.org.uk> ++ * Phillip Lougher <phillip@lougher.demon.co.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License @@ -4313,6 +4168,8 @@ + struct meta_index *meta_index; + z_stream stream; + long long *inode_lookup_table; ++ int unused_cache_blks; ++ int unused_frag_blks; + int (*read_inode)(struct inode *i, squashfs_inode_t \ + inode); + long long (*read_blocklist)(struct inode *inode, int \ @@ -4321,8 +4178,9 @@ + int (*read_fragment_index_table)(struct super_block *s); +}; +#endif ---- linux-2.6.24-rc1.orig/init/do_mounts_rd.c -+++ linux-2.6.24-rc1/init/do_mounts_rd.c +diff -x .gitignore -Nurp linux-2.6.24/init/do_mounts_rd.c linux-2.6.24-squashfs3.3/init/do_mounts_rd.c +--- linux-2.6.24/init/do_mounts_rd.c 2007-10-25 17:41:49.000000000 +0100 ++++ linux-2.6.24-squashfs3.3/init/do_mounts_rd.c 2007-11-01 05:06:25.000000000 +0000 @@ -5,6 +5,7 @@ #include <linux/ext2_fs.h> #include <linux/romfs_fs.h> @@ -4331,7 +4189,7 @@ #include <linux/initrd.h> #include <linux/string.h> -@@ -39,6 +40,7 @@ +@@ -39,6 +40,7 @@ static int __init crd_load(int in_fd, in * numbers could not be found. * * We currently check for the following magic numbers: @@ -4339,7 +4197,7 @@ * minix * ext2 * romfs -@@ -53,6 +55,7 @@ +@@ -53,6 +55,7 @@ identify_ramdisk_image(int fd, int start struct ext2_super_block *ext2sb; struct romfs_super_block *romfsb; struct cramfs_super *cramfsb; @@ -4347,7 +4205,7 @@ int nblocks = -1; unsigned char *buf; -@@ -64,6 +67,7 @@ +@@ -64,6 +67,7 @@ identify_ramdisk_image(int fd, int start ext2sb = (struct ext2_super_block *) buf; romfsb = (struct romfs_super_block *) buf; cramfsb = (struct cramfs_super *) buf; @@ -4355,7 +4213,7 @@ memset(buf, 0xe5, size); /* -@@ -101,6 +105,18 @@ +@@ -101,6 +105,18 @@ identify_ramdisk_image(int fd, int start goto done; } diff --git a/meta/packages/linux/linux-rp-2.6.23+2.6.24-rc8/uvesafb-0.1-rc3-2.6.22.patch b/meta/packages/linux/linux-rp-2.6.23+2.6.24-rc8/uvesafb-0.1-rc3-2.6.22.patch deleted file mode 100644 index 711375114f..0000000000 --- a/meta/packages/linux/linux-rp-2.6.23+2.6.24-rc8/uvesafb-0.1-rc3-2.6.22.patch +++ /dev/null @@ -1,2590 +0,0 @@ ---- - Documentation/fb/uvesafb.txt | 188 +++ - drivers/video/Kconfig | 18 - drivers/video/Makefile | 1 - drivers/video/modedb.c | 28 - drivers/video/uvesafb.c | 2058 +++++++++++++++++++++++++++++++++++++++++++ - include/linux/connector.h | 7 - include/video/Kbuild | 2 - include/video/uvesafb.h | 193 ++++ - 8 files changed, 2479 insertions(+), 16 deletions(-) - -Index: linux-2.6.22/Documentation/fb/uvesafb.txt -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/Documentation/fb/uvesafb.txt 2007-08-28 21:56:34.000000000 +0100 -@@ -0,0 +1,188 @@ -+ -+uvesafb - A Generic Driver for VBE2+ compliant video cards -+========================================================== -+ -+1. Requirements -+--------------- -+ -+uvesafb should work with any video card that has a Video BIOS compliant -+with the VBE 2.0 standard. -+ -+Unlike other drivers, uvesafb makes use of a userspace helper called -+v86d. v86d is used to run the x86 Video BIOS code in a simulated and -+controlled environment. This allows uvesafb to function on arches other -+than x86. Check the v86d documentation for a list of currently supported -+arches. -+ -+v86d source code can be downloaded from the following website: -+ http://dev.gentoo.org/~spock/projects/uvesafb -+ -+Please refer to the v86d documentation for detailed configuration and -+installation instructions. -+ -+Note that the v86d userspace helper has to be available at all times in -+order for uvesafb to work properly. If you want to use uvesafb during -+early boot, you will have to include v86d into an initramfs image, and -+either compile it into the kernel or use it as an initrd. -+ -+2. Caveats and limitations -+-------------------------- -+ -+uvesafb is a _generic_ driver which supports a wide variety of video -+cards, but which is ultimately limited by the Video BIOS interface. -+The most important limitations are: -+ -+- Lack of any type of acceleration. -+- A strict and limited set of supported video modes. Often the native -+ or most optimal resolution/refresh rate for your setup will not work -+ with uvesafb, simply because the Video BIOS doesn't support the -+ video mode you want to use. This can be especially painful with -+ widescreen panels, where native video modes don't have the 4:3 aspect -+ ratio, which is what most BIOS-es are limited to. -+- Adjusting the refresh rate is only possible with a VBE 3.0 compliant -+ Video BIOS. Note that many nVidia Video BIOS-es claim to be VBE 3.0 -+ compliant, while they simply ignore any refresh rate settings. -+ -+3. Configuration -+---------------- -+ -+uvesafb can be compiled either as a module, or directly into the kernel. -+In both cases it supports the same set of configuration options, which -+are either given on the kernel command line or as module parameters, e.g.: -+ -+ video=uvesafb:1024x768-32,mtrr:3,ywrap (compiled into the kernel) -+ -+ # modprobe uvesafb mode=1024x768-32 mtrr=3 scroll=ywrap (module) -+ -+Accepted options: -+ -+ypan Enable display panning using the VESA protected mode -+ interface. The visible screen is just a window of the -+ video memory, console scrolling is done by changing the -+ start of the window. Available on x86 only. -+ -+ywrap Same as ypan, but assumes your gfx board can wrap-around -+ the video memory (i.e. starts reading from top if it -+ reaches the end of video memory). Faster than ypan. -+ Available on x86 only. -+ -+redraw Scroll by redrawing the affected part of the screen, this -+ is the safe (and slow) default. -+ -+(If you're using uvesafb as a module, the above three options are -+ used a parameter of the scroll option, e.g. scroll=ypan.) -+ -+vgapal Use the standard VGA registers for palette changes. -+ -+pmipal Use the protected mode interface for palette changes. -+ This is the default if the protected mode interface is -+ available. Available on x86 only. -+ -+mtrr:n Setup memory type range registers for the framebuffer -+ where n: -+ 0 - disabled (equivalent to nomtrr) (default) -+ 1 - uncachable -+ 2 - write-back -+ 3 - write-combining -+ 4 - write-through -+ -+ If you see the following in dmesg, choose the type that matches -+ the old one. In this example, use "mtrr:2". -+... -+mtrr: type mismatch for e0000000,8000000 old: write-back new: write-combining -+... -+ -+nomtrr Do not use memory type range registers. -+ -+vremap:n -+ Remap 'n' MiB of video RAM. If 0 or not specified, remap memory -+ according to video mode. -+ -+vtotal:n -+ If the video BIOS of your card incorrectly determines the total -+ amount of video RAM, use this option to override the BIOS (in MiB). -+ -+<mode> The mode you want to set, in the standard modedb format. Refer to -+ modedb.txt for a detailed description. When uvesafb is compiled as -+ a module, the mode string should be provided as a value of the -+ 'mode' option. -+ -+vbemode:x -+ Force the use of VBE mode x. The mode will only be set if it's -+ found in the VBE-provided list of supported modes. -+ NOTE: The mode number 'x' should be specified in VESA mode number -+ notation, not the Linux kernel one (eg. 257 instead of 769). -+ HINT: If you use this option because normal <mode> parameter does -+ not work for you and you use a X server, you'll probably want to -+ set the 'nocrtc' option to ensure that the video mode is properly -+ restored after console <-> X switches. -+ -+nocrtc Do not use CRTC timings while setting the video mode. This option -+ has any effect only if the Video BIOS is VBE 3.0 compliant. Use it -+ if you have problems with modes set the standard way. Note that -+ using this option implies that any refresh rate adjustments will -+ be ignored and the refresh rate will stay at your BIOS default (60 Hz). -+ -+noedid Do not try to fetch and use EDID-provided modes. -+ -+noblank Disable hardware blanking. -+ -+v86d:path -+ Set path to the v86d executable. This option is only available as -+ a module parameter, and not as a part of the video= string. If you -+ need to use it and have uvesafb built into the kernel, use -+ uvesafb.v86d="path". -+ -+Additionally, the following parameters may be provided. They all override the -+EDID-provided values and BIOS defaults. Refer to your monitor's specs to get -+the correct values for maxhf, maxvf and maxclk for your hardware. -+ -+maxhf:n Maximum horizontal frequency (in kHz). -+maxvf:n Maximum vertical frequency (in Hz). -+maxclk:n Maximum pixel clock (in MHz). -+ -+4. The sysfs interface -+---------------------- -+ -+uvesafb provides several sysfs nodes for configurable parameters and -+additional information. -+ -+Driver attributes: -+ -+/sys/bus/platform/drivers/uvesafb -+ - v86d (default: /sbin/v86d) -+ Path to the v86d executable. v86d is started by uvesafb -+ if an instance of the daemon isn't already running. -+ -+Device attributes: -+ -+/sys/bus/platform/drivers/uvesafb/uvesafb.0 -+ - nocrtc -+ Use the default refresh rate (60 Hz) if set to 1. -+ -+ - oem_product_name -+ - oem_product_rev -+ - oem_string -+ - oem_vendor -+ Information about the card and its maker. -+ -+ - vbe_modes -+ A list of video modes supported by the Video BIOS along with their -+ VBE mode numbers in hex. -+ -+ - vbe_version -+ A BCD value indicating the implemented VBE standard. -+ -+5. Miscellaneous -+---------------- -+ -+Uvesafb will set a video mode with the default refresh rate and timings -+from the Video BIOS if you set pixclock to 0 in fb_var_screeninfo. -+ -+ -+-- -+ Michal Januszewski <spock@gentoo.org> -+ Last updated: 2007-06-16 -+ -+ Documentation of the uvesafb options is loosely based on vesafb.txt. -+ -Index: linux-2.6.22/drivers/video/Kconfig -=================================================================== ---- linux-2.6.22.orig/drivers/video/Kconfig 2007-08-28 21:56:33.000000000 +0100 -+++ linux-2.6.22/drivers/video/Kconfig 2007-08-28 21:56:34.000000000 +0100 -@@ -592,6 +592,24 @@ config FB_TGA - - Say Y if you have one of those. - -+config FB_UVESA -+ tristate "Userspace VESA VGA graphics support" -+ depends on FB && CONNECTOR -+ select FB_CFB_FILLRECT -+ select FB_CFB_COPYAREA -+ select FB_CFB_IMAGEBLIT -+ select FB_MODE_HELPERS -+ help -+ This is the frame buffer driver for generic VBE 2.0 compliant -+ graphic cards. It can also take advantage of VBE 3.0 features, -+ such as refresh rate adjustment. -+ -+ This driver generally provides more features than vesafb but -+ requires a userspace helper application called 'v86d'. See -+ <file:Documentation/fb/uvesafb.txt> for more information. -+ -+ If unsure, say N. -+ - config FB_VESA - bool "VESA VGA graphics support" - depends on (FB = y) && X86 -Index: linux-2.6.22/drivers/video/Makefile -=================================================================== ---- linux-2.6.22.orig/drivers/video/Makefile 2007-08-28 21:56:33.000000000 +0100 -+++ linux-2.6.22/drivers/video/Makefile 2007-08-28 21:56:34.000000000 +0100 -@@ -116,6 +116,7 @@ obj-$(CONFIG_FB_XILINX) += xil - obj-$(CONFIG_FB_OMAP) += omap/ - - # Platform or fallback drivers go here -+obj-$(CONFIG_FB_UVESA) += uvesafb.o - obj-$(CONFIG_FB_VESA) += vesafb.o - obj-$(CONFIG_FB_IMAC) += imacfb.o - obj-$(CONFIG_FB_VGA16) += vga16fb.o -Index: linux-2.6.22/drivers/video/modedb.c -=================================================================== ---- linux-2.6.22.orig/drivers/video/modedb.c 2007-08-28 21:54:13.000000000 +0100 -+++ linux-2.6.22/drivers/video/modedb.c 2007-08-28 21:56:34.000000000 +0100 -@@ -606,26 +606,29 @@ done: - DPRINTK("Trying specified video mode%s %ix%i\n", - refresh_specified ? "" : " (ignoring refresh rate)", xres, yres); - -- diff = refresh; -+ if (!refresh_specified) -+ diff = 0; -+ else -+ diff = refresh; -+ - best = -1; - for (i = 0; i < dbsize; i++) { -- if (name_matches(db[i], name, namelen) || -- (res_specified && res_matches(db[i], xres, yres))) { -- if(!fb_try_mode(var, info, &db[i], bpp)) { -- if(!refresh_specified || db[i].refresh == refresh) -- return 1; -- else { -- if(diff > abs(db[i].refresh - refresh)) { -- diff = abs(db[i].refresh - refresh); -- best = i; -- } -+ if ((name_matches(db[i], name, namelen) || -+ (res_specified && res_matches(db[i], xres, yres))) && -+ !fb_try_mode(var, info, &db[i], bpp)) { -+ if (refresh_specified && db[i].refresh == refresh) { -+ return 1; -+ } else { -+ if (diff < db[i].refresh) { -+ diff = db[i].refresh; -+ best = i; - } - } - } - } - if (best != -1) { - fb_try_mode(var, info, &db[best], bpp); -- return 2; -+ return (refresh_specified) ? 2 : 1; - } - - diff = xres + yres; -@@ -938,6 +941,7 @@ void fb_destroy_modelist(struct list_hea - kfree(pos); - } - } -+EXPORT_SYMBOL_GPL(fb_destroy_modelist); - - /** - * fb_videomode_to_modelist: convert mode array to mode list -Index: linux-2.6.22/drivers/video/uvesafb.c -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/drivers/video/uvesafb.c 2007-08-28 21:56:34.000000000 +0100 -@@ -0,0 +1,2058 @@ -+/* -+ * A framebuffer driver for VBE 2.0+ compliant video cards -+ * -+ * (c) 2007 Michal Januszewski <spock@gentoo.org> -+ * Loosely based upon the vesafb driver. -+ * -+ */ -+#include <linux/init.h> -+#include <linux/module.h> -+#include <linux/moduleparam.h> -+#include <linux/skbuff.h> -+#include <linux/timer.h> -+#include <linux/completion.h> -+#include <linux/connector.h> -+#include <linux/random.h> -+#include <linux/platform_device.h> -+#include <linux/limits.h> -+#include <linux/fb.h> -+#include <linux/io.h> -+#include <linux/mutex.h> -+#include <video/edid.h> -+#include <video/vga.h> -+#include <video/uvesafb.h> -+#ifdef CONFIG_MTRR -+#include <asm/mtrr.h> -+#endif -+#include "edid.h" -+ -+static struct cb_id uvesafb_cn_id = { -+ .idx = CN_IDX_V86D, -+ .val = CN_VAL_V86D_UVESAFB -+}; -+static char v86d_path[PATH_MAX] = "/sbin/v86d"; -+static char v86d_started; /* has v86d been started by uvesafb? */ -+ -+static struct fb_fix_screeninfo uvesafb_fix __devinitdata = { -+ .id = "VESA VGA", -+ .type = FB_TYPE_PACKED_PIXELS, -+ .accel = FB_ACCEL_NONE, -+ .visual = FB_VISUAL_TRUECOLOR, -+}; -+ -+static int mtrr __devinitdata = 3; /* enable mtrr by default */ -+static int blank __devinitdata = 1; /* enable blanking by default */ -+static int ypan __devinitdata = 1; /* 0: scroll, 1: ypan, 2: ywrap */ -+static int pmi_setpal __devinitdata = 1; /* use PMI for palette changes */ -+static int nocrtc __devinitdata; /* ignore CRTC settings */ -+static int noedid __devinitdata; /* don't try DDC transfers */ -+static int vram_remap __devinitdata; /* set amt. of memory to be used */ -+static int vram_total __devinitdata; /* set total amount of memory */ -+static u16 maxclk __devinitdata; /* maximum pixel clock */ -+static u16 maxvf __devinitdata; /* maximum vertical frequency */ -+static u16 maxhf __devinitdata; /* maximum horizontal frequency */ -+static u16 vbemode __devinitdata; /* force use of a specific VBE mode */ -+static char *mode_option __devinitdata; -+ -+static struct uvesafb_ktask *uvfb_tasks[UVESAFB_TASKS_MAX]; -+static DEFINE_MUTEX(uvfb_lock); -+ -+/* -+ * A handler for replies from userspace. -+ * -+ * Make sure each message passes consistency checks and if it does, -+ * find the kernel part of the task struct, copy the registers and -+ * the buffer contents and then complete the task. -+ */ -+static void uvesafb_cn_callback(void *data) -+{ -+ struct cn_msg *msg = data; -+ struct uvesafb_task *utask; -+ struct uvesafb_ktask *task; -+ -+ if (msg->seq >= UVESAFB_TASKS_MAX) -+ return; -+ -+ mutex_lock(&uvfb_lock); -+ task = uvfb_tasks[msg->seq]; -+ -+ if (!task || msg->ack != task->ack) { -+ mutex_unlock(&uvfb_lock); -+ return; -+ } -+ -+ utask = (struct uvesafb_task *)msg->data; -+ -+ /* Sanity checks for the buffer length. */ -+ if (task->t.buf_len < utask->buf_len || -+ utask->buf_len > msg->len - sizeof(*utask)) { -+ mutex_unlock(&uvfb_lock); -+ return; -+ } -+ -+ uvfb_tasks[msg->seq] = NULL; -+ mutex_unlock(&uvfb_lock); -+ -+ memcpy(&task->t, utask, sizeof(*utask)); -+ -+ if (task->t.buf_len && task->buf) -+ memcpy(task->buf, utask + 1, task->t.buf_len); -+ -+ complete(task->done); -+ return; -+} -+ -+static int uvesafb_helper_start(void) -+{ -+ char *envp[] = { -+ "HOME=/", -+ "PATH=/sbin:/bin", -+ NULL, -+ }; -+ -+ char *argv[] = { -+ v86d_path, -+ NULL, -+ }; -+ -+ return call_usermodehelper(v86d_path, argv, envp, 1); -+} -+ -+/* -+ * Execute a uvesafb task. -+ * -+ * Returns 0 if the task is executed successfully. -+ * -+ * A message sent to the userspace consists of the uvesafb_task -+ * struct and (optionally) a buffer. The uvesafb_task struct is -+ * a simplified version of uvesafb_ktask (its kernel counterpart) -+ * containing only the register values, flags and the length of -+ * the buffer. -+ * -+ * Each message is assigned a sequence number (increased linearly) -+ * and a random ack number. The sequence number is used as a key -+ * for the uvfb_tasks array which holds pointers to uvesafb_ktask -+ * structs for all requests. -+ */ -+static int uvesafb_exec(struct uvesafb_ktask *task) -+{ -+ static int seq; -+ struct cn_msg *m; -+ int err; -+ int len = sizeof(task->t) + task->t.buf_len; -+ -+ /* -+ * Check whether the message isn't longer than the maximum -+ * allowed by connector. -+ */ -+ if (sizeof(*m) + len > CONNECTOR_MAX_MSG_SIZE) { -+ printk(KERN_WARNING "uvesafb: message too long (%d), " -+ "can't execute task\n", (int)(sizeof(*m) + len)); -+ return -E2BIG; -+ } -+ -+ m = kzalloc(sizeof(*m) + len, GFP_KERNEL); -+ if (!m) -+ return -ENOMEM; -+ -+ init_completion(task->done); -+ -+ memcpy(&m->id, &uvesafb_cn_id, sizeof(m->id)); -+ m->seq = seq; -+ m->len = len; -+ m->ack = random32(); -+ -+ /* uvesafb_task structure */ -+ memcpy(m + 1, &task->t, sizeof(task->t)); -+ -+ /* Buffer */ -+ memcpy((u8 *)(m + 1) + sizeof(task->t), task->buf, task->t.buf_len); -+ -+ /* -+ * Save the message ack number so that we can find the kernel -+ * part of this task when a reply is received from userspace. -+ */ -+ task->ack = m->ack; -+ -+ mutex_lock(&uvfb_lock); -+ -+ /* If all slots are taken -- bail out. */ -+ if (uvfb_tasks[seq]) { -+ mutex_unlock(&uvfb_lock); -+ return -EBUSY; -+ } -+ -+ /* Save a pointer to the kernel part of the task struct. */ -+ uvfb_tasks[seq] = task; -+ mutex_unlock(&uvfb_lock); -+ -+ err = cn_netlink_send(m, 0, gfp_any()); -+ if (err == -ESRCH) { -+ /* -+ * Try to start the userspace helper if sending -+ * the request failed the first time. -+ */ -+ err = uvesafb_helper_start(); -+ if (err) { -+ printk(KERN_ERR "uvesafb: failed to execute %s\n", -+ v86d_path); -+ printk(KERN_ERR "uvesafb: make sure that the v86d " -+ "helper is installed and executable\n"); -+ } else { -+ v86d_started = 1; -+ err = cn_netlink_send(m, 0, gfp_any()); -+ } -+ } -+ kfree(m); -+ -+ if (!err && !(task->t.flags & TF_EXIT)) -+ err = !wait_for_completion_timeout(task->done, -+ msecs_to_jiffies(UVESAFB_TIMEOUT)); -+ -+ mutex_lock(&uvfb_lock); -+ uvfb_tasks[seq] = NULL; -+ mutex_unlock(&uvfb_lock); -+ -+ seq++; -+ if (seq >= UVESAFB_TASKS_MAX) -+ seq = 0; -+ -+ return err; -+} -+ -+/* -+ * Free a uvesafb_ktask struct. -+ */ -+static void uvesafb_free(struct uvesafb_ktask *task) -+{ -+ if (task) { -+ if (task->done) -+ kfree(task->done); -+ kfree(task); -+ } -+} -+ -+/* -+ * Prepare a uvesafb_ktask struct to be used again. -+ */ -+static void uvesafb_reset(struct uvesafb_ktask *task) -+{ -+ struct completion *cpl = task->done; -+ -+ memset(task, 0, sizeof(*task)); -+ task->done = cpl; -+} -+ -+/* -+ * Allocate and prepare a uvesafb_ktask struct. -+ */ -+static struct uvesafb_ktask *uvesafb_prep(void) -+{ -+ struct uvesafb_ktask *task; -+ -+ task = kzalloc(sizeof(*task), GFP_KERNEL); -+ if (task) { -+ task->done = kzalloc(sizeof(*task->done), GFP_KERNEL); -+ if (!task->done) { -+ kfree(task); -+ task = NULL; -+ } -+ } -+ return task; -+} -+ -+static void uvesafb_setup_var(struct fb_var_screeninfo *var, -+ struct fb_info *info, struct vbe_mode_ib *mode) -+{ -+ struct uvesafb_par *par = info->par; -+ -+ var->vmode = FB_VMODE_NONINTERLACED; -+ var->sync = FB_SYNC_VERT_HIGH_ACT; -+ -+ var->xres = mode->x_res; -+ var->yres = mode->y_res; -+ var->xres_virtual = mode->x_res; -+ var->yres_virtual = (par->ypan) ? -+ info->fix.smem_len / mode->bytes_per_scan_line : -+ mode->y_res; -+ var->xoffset = 0; -+ var->yoffset = 0; -+ var->bits_per_pixel = mode->bits_per_pixel; -+ -+ if (var->bits_per_pixel == 15) -+ var->bits_per_pixel = 16; -+ -+ if (var->bits_per_pixel > 8) { -+ var->red.offset = mode->red_off; -+ var->red.length = mode->red_len; -+ var->green.offset = mode->green_off; -+ var->green.length = mode->green_len; -+ var->blue.offset = mode->blue_off; -+ var->blue.length = mode->blue_len; -+ var->transp.offset = mode->rsvd_off; -+ var->transp.length = mode->rsvd_len; -+ } else { -+ var->red.offset = 0; -+ var->green.offset = 0; -+ var->blue.offset = 0; -+ var->transp.offset = 0; -+ -+ /* -+ * We're assuming that we can switch the DAC to 8 bits. If -+ * this proves to be incorrect, we'll update the fields -+ * later in set_par(). -+ */ -+ if (par->vbe_ib.capabilities & VBE_CAP_CAN_SWITCH_DAC) { -+ var->red.length = 8; -+ var->green.length = 8; -+ var->blue.length = 8; -+ var->transp.length = 0; -+ } else { -+ var->red.length = 6; -+ var->green.length = 6; -+ var->blue.length = 6; -+ var->transp.length = 0; -+ } -+ } -+} -+ -+static int uvesafb_vbe_find_mode(struct uvesafb_par *par, -+ int xres, int yres, int depth, unsigned char flags) -+{ -+ int i, match = -1, h = 0, d = 0x7fffffff; -+ -+ for (i = 0; i < par->vbe_modes_cnt; i++) { -+ h = abs(par->vbe_modes[i].x_res - xres) + -+ abs(par->vbe_modes[i].y_res - yres) + -+ abs(depth - par->vbe_modes[i].depth); -+ -+ /* -+ * We have an exact match in terms of resolution -+ * and depth. -+ */ -+ if (h == 0) -+ return i; -+ -+ if (h < d || (h == d && par->vbe_modes[i].depth > depth)) { -+ d = h; -+ match = i; -+ } -+ } -+ i = 1; -+ -+ if (flags & UVESAFB_EXACT_DEPTH && -+ par->vbe_modes[match].depth != depth) -+ i = 0; -+ -+ if (flags & UVESAFB_EXACT_RES && d > 24) -+ i = 0; -+ -+ if (i != 0) -+ return match; -+ else -+ return -1; -+} -+ -+static u8 *uvesafb_vbe_state_save(struct uvesafb_par *par) -+{ -+ struct uvesafb_ktask *task; -+ u8 *state; -+ int err; -+ -+ if (!par->vbe_state_size) -+ return NULL; -+ -+ state = kmalloc(par->vbe_state_size, GFP_KERNEL); -+ if (!state) -+ return NULL; -+ -+ task = uvesafb_prep(); -+ if (!task) { -+ kfree(state); -+ return NULL; -+ } -+ -+ task->t.regs.eax = 0x4f04; -+ task->t.regs.ecx = 0x000f; -+ task->t.regs.edx = 0x0001; -+ task->t.flags = TF_BUF_RET | TF_BUF_ESBX; -+ task->t.buf_len = par->vbe_state_size; -+ task->buf = state; -+ err = uvesafb_exec(task); -+ -+ if (err || (task->t.regs.eax & 0xffff) != 0x004f) { -+ printk(KERN_WARNING "uvesafb: VBE get state call " -+ "failed (eax=0x%x, err=%d)\n", -+ task->t.regs.eax, err); -+ kfree(state); -+ state = NULL; -+ } -+ -+ uvesafb_free(task); -+ return state; -+} -+ -+static void uvesafb_vbe_state_restore(struct uvesafb_par *par, u8 *state_buf) -+{ -+ struct uvesafb_ktask *task; -+ int err; -+ -+ if (!state_buf) -+ return; -+ -+ task = uvesafb_prep(); -+ if (!task) -+ return; -+ -+ task->t.regs.eax = 0x4f04; -+ task->t.regs.ecx = 0x000f; -+ task->t.regs.edx = 0x0002; -+ task->t.buf_len = par->vbe_state_size; -+ task->t.flags = TF_BUF_ESBX; -+ task->buf = state_buf; -+ -+ err = uvesafb_exec(task); -+ if (err || (task->t.regs.eax & 0xffff) != 0x004f) -+ printk(KERN_WARNING "uvesafb: VBE state restore call " -+ "failed (eax=0x%x, err=%d)\n", -+ task->t.regs.eax, err); -+ -+ uvesafb_free(task); -+} -+ -+static int __devinit uvesafb_vbe_getinfo(struct uvesafb_ktask *task, -+ struct uvesafb_par *par) -+{ -+ int err; -+ -+ task->t.regs.eax = 0x4f00; -+ task->t.flags = TF_VBEIB; -+ task->t.buf_len = sizeof(struct vbe_ib); -+ task->buf = &par->vbe_ib; -+ strncpy(par->vbe_ib.vbe_signature, "VBE2", 4); -+ -+ err = uvesafb_exec(task); -+ if (err || (task->t.regs.eax & 0xffff) != 0x004f) { -+ printk(KERN_ERR "uvesafb: Getting VBE info block failed " -+ "(eax=0x%x, err=%d)\n", (u32)task->t.regs.eax, -+ err); -+ return -EINVAL; -+ } -+ -+ if (par->vbe_ib.vbe_version < 0x0200) { -+ printk(KERN_ERR "uvesafb: Sorry, pre-VBE 2.0 cards are " -+ "not supported.\n"); -+ return -EINVAL; -+ } -+ -+ if (!par->vbe_ib.mode_list_ptr) { -+ printk(KERN_ERR "uvesafb: Missing mode list!\n"); -+ return -EINVAL; -+ } -+ -+ printk(KERN_INFO "uvesafb: "); -+ -+ /* -+ * Convert string pointers and the mode list pointer into -+ * usable addresses. Print informational messages about the -+ * video adapter and its vendor. -+ */ -+ if (par->vbe_ib.oem_vendor_name_ptr) -+ printk("%s, ", -+ ((char *)task->buf) + par->vbe_ib.oem_vendor_name_ptr); -+ -+ if (par->vbe_ib.oem_product_name_ptr) -+ printk("%s, ", -+ ((char *)task->buf) + par->vbe_ib.oem_product_name_ptr); -+ -+ if (par->vbe_ib.oem_product_rev_ptr) -+ printk("%s, ", -+ ((char *)task->buf) + par->vbe_ib.oem_product_rev_ptr); -+ -+ if (par->vbe_ib.oem_string_ptr) -+ printk("OEM: %s, ", -+ ((char *)task->buf) + par->vbe_ib.oem_string_ptr); -+ -+ printk("VBE v%d.%d\n", ((par->vbe_ib.vbe_version & 0xff00) >> 8), -+ par->vbe_ib.vbe_version & 0xff); -+ -+ return 0; -+} -+ -+static int __devinit uvesafb_vbe_getmodes(struct uvesafb_ktask *task, -+ struct uvesafb_par *par) -+{ -+ int off = 0, err; -+ u16 *mode; -+ -+ par->vbe_modes_cnt = 0; -+ -+ /* Count available modes. */ -+ mode = (u16 *) (((u8 *)&par->vbe_ib) + par->vbe_ib.mode_list_ptr); -+ while (*mode != 0xffff) { -+ par->vbe_modes_cnt++; -+ mode++; -+ } -+ -+ par->vbe_modes = kzalloc(sizeof(struct vbe_mode_ib) * -+ par->vbe_modes_cnt, GFP_KERNEL); -+ if (!par->vbe_modes) -+ return -ENOMEM; -+ -+ /* Get info about all available modes. */ -+ mode = (u16 *) (((u8 *)&par->vbe_ib) + par->vbe_ib.mode_list_ptr); -+ while (*mode != 0xffff) { -+ struct vbe_mode_ib *mib; -+ -+ uvesafb_reset(task); -+ task->t.regs.eax = 0x4f01; -+ task->t.regs.ecx = (u32) *mode; -+ task->t.flags = TF_BUF_RET | TF_BUF_ESDI; -+ task->t.buf_len = sizeof(struct vbe_mode_ib); -+ task->buf = par->vbe_modes + off; -+ -+ err = uvesafb_exec(task); -+ if (err || (task->t.regs.eax & 0xffff) != 0x004f) { -+ printk(KERN_ERR "uvesafb: Getting mode info block " -+ "for mode 0x%x failed (eax=0x%x, err=%d)\n", -+ *mode, (u32)task->t.regs.eax, err); -+ return -EINVAL; -+ } -+ -+ mib = task->buf; -+ mib->mode_id = *mode; -+ -+ /* -+ * We only want modes that are supported with the current -+ * hardware configuration, color, graphics and that have -+ * support for the LFB. -+ */ -+ if ((mib->mode_attr & VBE_MODE_MASK) == VBE_MODE_MASK && -+ mib->bits_per_pixel >= 8) -+ off++; -+ else -+ par->vbe_modes_cnt--; -+ -+ mode++; -+ mib->depth = mib->red_len + mib->green_len + mib->blue_len; -+ -+ /* -+ * Handle 8bpp modes and modes with broken color component -+ * lengths. -+ */ -+ if (mib->depth == 0 || (mib->depth == 24 && -+ mib->bits_per_pixel == 32)) -+ mib->depth = mib->bits_per_pixel; -+ } -+ -+ return 0; -+} -+ -+/* -+ * The Protected Mode Interface is 32-bit x86 code, so we only run it on -+ * x86 and not x86_64. -+ */ -+#ifdef CONFIG_X86_32 -+static int __devinit uvesafb_vbe_getpmi(struct uvesafb_ktask *task, -+ struct uvesafb_par *par) -+{ -+ int i, err; -+ -+ uvesafb_reset(task); -+ task->t.regs.eax = 0x4f0a; -+ task->t.regs.ebx = 0x0; -+ err = uvesafb_exec(task); -+ -+ if ((task->t.regs.eax & 0xffff) != 0x4f || task->t.regs.es < 0xc000) { -+ par->pmi_setpal = par->ypan = 0; -+ } else { -+ par->pmi_base = (u16 *)phys_to_virt(((u32)task->t.regs.es << 4) -+ + task->t.regs.edi); -+ par->pmi_start = (u8 *)par->pmi_base + par->pmi_base[1]; -+ par->pmi_pal = (u8 *)par->pmi_base + par->pmi_base[2]; -+ printk(KERN_INFO "uvesafb: protected mode interface info at " -+ "%04x:%04x\n", -+ (u16)task->t.regs.es, (u16)task->t.regs.edi); -+ printk(KERN_INFO "uvesafb: pmi: set display start = %p, " -+ "set palette = %p\n", par->pmi_start, -+ par->pmi_pal); -+ -+ if (par->pmi_base[3]) { -+ printk(KERN_INFO "uvesafb: pmi: ports = "); -+ for (i = par->pmi_base[3]/2; -+ par->pmi_base[i] != 0xffff; i++) -+ printk("%x ", par->pmi_base[i]); -+ printk("\n"); -+ -+ if (par->pmi_base[i] != 0xffff) { -+ printk(KERN_INFO "uvesafb: can't handle memory" -+ " requests, pmi disabled\n"); -+ par->ypan = par->pmi_setpal = 0; -+ } -+ } -+ } -+ return 0; -+} -+#endif /* CONFIG_X86_32 */ -+ -+/* -+ * Check whether a video mode is supported by the Video BIOS and is -+ * compatible with the monitor limits. -+ */ -+static int __devinit uvesafb_is_valid_mode(struct fb_videomode *mode, -+ struct fb_info *info) -+{ -+ if (info->monspecs.gtf) { -+ fb_videomode_to_var(&info->var, mode); -+ if (fb_validate_mode(&info->var, info)) -+ return 0; -+ } -+ -+ if (uvesafb_vbe_find_mode(info->par, mode->xres, mode->yres, 8, -+ UVESAFB_EXACT_RES) == -1) -+ return 0; -+ -+ return 1; -+} -+ -+static int __devinit uvesafb_vbe_getedid(struct uvesafb_ktask *task, -+ struct fb_info *info) -+{ -+ struct uvesafb_par *par = info->par; -+ int err = 0; -+ -+ if (noedid || par->vbe_ib.vbe_version < 0x0300) -+ return -EINVAL; -+ -+ task->t.regs.eax = 0x4f15; -+ task->t.regs.ebx = 0; -+ task->t.regs.ecx = 0; -+ task->t.buf_len = 0; -+ task->t.flags = 0; -+ -+ err = uvesafb_exec(task); -+ -+ if ((task->t.regs.eax & 0xffff) != 0x004f || err) -+ return -EINVAL; -+ -+ if ((task->t.regs.ebx & 0x3) == 3) { -+ printk(KERN_INFO "uvesafb: VBIOS/hardware supports both " -+ "DDC1 and DDC2 transfers\n"); -+ } else if ((task->t.regs.ebx & 0x3) == 2) { -+ printk(KERN_INFO "uvesafb: VBIOS/hardware supports DDC2 " -+ "transfers\n"); -+ } else if ((task->t.regs.ebx & 0x3) == 1) { -+ printk(KERN_INFO "uvesafb: VBIOS/hardware supports DDC1 " -+ "transfers\n"); -+ } else { -+ printk(KERN_INFO "uvesafb: VBIOS/hardware doesn't support " -+ "DDC transfers\n"); -+ return -EINVAL; -+ } -+ -+ task->t.regs.eax = 0x4f15; -+ task->t.regs.ebx = 1; -+ task->t.regs.ecx = task->t.regs.edx = 0; -+ task->t.flags = TF_BUF_RET | TF_BUF_ESDI; -+ task->t.buf_len = EDID_LENGTH; -+ task->buf = kzalloc(EDID_LENGTH, GFP_KERNEL); -+ -+ err = uvesafb_exec(task); -+ -+ if ((task->t.regs.eax & 0xffff) == 0x004f && !err) { -+ fb_edid_to_monspecs(task->buf, &info->monspecs); -+ -+ if (info->monspecs.vfmax && info->monspecs.hfmax) { -+ /* -+ * If the maximum pixel clock wasn't specified in -+ * the EDID block, set it to 300 MHz. -+ */ -+ if (info->monspecs.dclkmax == 0) -+ info->monspecs.dclkmax = 300 * 1000000; -+ info->monspecs.gtf = 1; -+ } -+ } else { -+ err = -EINVAL; -+ } -+ -+ kfree(task->buf); -+ return err; -+} -+ -+static void __devinit uvesafb_vbe_getmonspecs(struct uvesafb_ktask *task, -+ struct fb_info *info) -+{ -+ struct uvesafb_par *par = info->par; -+ int i; -+ -+ memset(&info->monspecs, 0, sizeof(info->monspecs)); -+ -+ /* -+ * If we don't get all necessary data from the EDID block, -+ * mark it as incompatible with the GTF and set nocrtc so -+ * that we always use the default BIOS refresh rate. -+ */ -+ if (uvesafb_vbe_getedid(task, info)) { -+ info->monspecs.gtf = 0; -+ par->nocrtc = 1; -+ } -+ -+ /* Kernel command line overrides. */ -+ if (maxclk) -+ info->monspecs.dclkmax = maxclk * 1000000; -+ if (maxvf) -+ info->monspecs.vfmax = maxvf; -+ if (maxhf) -+ info->monspecs.hfmax = maxhf * 1000; -+ -+ /* -+ * In case DDC transfers are not supported, the user can provide -+ * monitor limits manually. Lower limits are set to "safe" values. -+ */ -+ if (info->monspecs.gtf == 0 && maxclk && maxvf && maxhf) { -+ info->monspecs.dclkmin = 0; -+ info->monspecs.vfmin = 60; -+ info->monspecs.hfmin = 29000; -+ info->monspecs.gtf = 1; -+ par->nocrtc = 0; -+ } -+ -+ if (info->monspecs.gtf) -+ printk(KERN_INFO -+ "uvesafb: monitor limits: vf = %d Hz, hf = %d kHz, " -+ "clk = %d MHz\n", info->monspecs.vfmax, -+ (int)(info->monspecs.hfmax / 1000), -+ (int)(info->monspecs.dclkmax / 1000000)); -+ else -+ printk(KERN_INFO "uvesafb: no monitor limits have been set, " -+ "default refresh rate will be used\n"); -+ -+ /* Add VBE modes to the modelist. */ -+ for (i = 0; i < par->vbe_modes_cnt; i++) { -+ struct fb_var_screeninfo var; -+ struct vbe_mode_ib *mode; -+ struct fb_videomode vmode; -+ -+ mode = &par->vbe_modes[i]; -+ memset(&var, 0, sizeof(var)); -+ -+ var.xres = mode->x_res; -+ var.yres = mode->y_res; -+ -+ fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, 60, &var, info); -+ fb_var_to_videomode(&vmode, &var); -+ fb_add_videomode(&vmode, &info->modelist); -+ } -+ -+ /* Add valid VESA modes to our modelist. */ -+ for (i = 0; i < VESA_MODEDB_SIZE; i++) { -+ if (uvesafb_is_valid_mode((struct fb_videomode *) -+ &vesa_modes[i], info)) -+ fb_add_videomode(&vesa_modes[i], &info->modelist); -+ } -+ -+ for (i = 0; i < info->monspecs.modedb_len; i++) { -+ if (uvesafb_is_valid_mode(&info->monspecs.modedb[i], info)) -+ fb_add_videomode(&info->monspecs.modedb[i], -+ &info->modelist); -+ } -+ -+ return; -+} -+ -+static void __devinit uvesafb_vbe_getstatesize(struct uvesafb_ktask *task, -+ struct uvesafb_par *par) -+{ -+ int err; -+ -+ uvesafb_reset(task); -+ -+ /* -+ * Get the VBE state buffer size. We want all available -+ * hardware state data (CL = 0x0f). -+ */ -+ task->t.regs.eax = 0x4f04; -+ task->t.regs.ecx = 0x000f; -+ task->t.regs.edx = 0x0000; -+ task->t.flags = 0; -+ -+ err = uvesafb_exec(task); -+ -+ if (err || (task->t.regs.eax & 0xffff) != 0x004f) { -+ printk(KERN_WARNING "uvesafb: VBE state buffer size " -+ "cannot be determined (eax=0x%x, err=%d)\n", -+ task->t.regs.eax, err); -+ par->vbe_state_size = 0; -+ return; -+ } -+ -+ par->vbe_state_size = 64 * (task->t.regs.ebx & 0xffff); -+} -+ -+static int __devinit uvesafb_vbe_init(struct fb_info *info) -+{ -+ struct uvesafb_ktask *task = NULL; -+ struct uvesafb_par *par = info->par; -+ int err; -+ -+ task = uvesafb_prep(); -+ if (!task) -+ return -ENOMEM; -+ -+ err = uvesafb_vbe_getinfo(task, par); -+ if (err) -+ goto out; -+ -+ err = uvesafb_vbe_getmodes(task, par); -+ if (err) -+ goto out; -+ -+ par->nocrtc = nocrtc; -+#ifdef CONFIG_X86_32 -+ par->pmi_setpal = pmi_setpal; -+ par->ypan = ypan; -+ -+ if (par->pmi_setpal || par->ypan) -+ uvesafb_vbe_getpmi(task, par); -+#else -+ /* The protected mode interface is not available on non-x86. */ -+ par->pmi_setpal = par->ypan = 0; -+#endif -+ -+ INIT_LIST_HEAD(&info->modelist); -+ uvesafb_vbe_getmonspecs(task, info); -+ uvesafb_vbe_getstatesize(task, par); -+ -+out: uvesafb_free(task); -+ return err; -+} -+ -+static int __devinit uvesafb_vbe_init_mode(struct fb_info *info) -+{ -+ struct list_head *pos; -+ struct fb_modelist *modelist; -+ struct fb_videomode *mode; -+ struct uvesafb_par *par = info->par; -+ int i, modeid; -+ -+ /* Has the user requested a specific VESA mode? */ -+ if (vbemode) { -+ for (i = 0; i < par->vbe_modes_cnt; i++) { -+ if (par->vbe_modes[i].mode_id == vbemode) { -+ fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, 60, -+ &info->var, info); -+ /* -+ * With pixclock set to 0, the default BIOS -+ * timings will be used in set_par(). -+ */ -+ info->var.pixclock = 0; -+ modeid = i; -+ goto gotmode; -+ } -+ } -+ printk(KERN_INFO "uvesafb: requested VBE mode 0x%x is " -+ "unavailable\n", vbemode); -+ vbemode = 0; -+ } -+ -+ /* Count the modes in the modelist */ -+ i = 0; -+ list_for_each(pos, &info->modelist) -+ i++; -+ -+ /* -+ * Convert the modelist into a modedb so that we can use it with -+ * fb_find_mode(). -+ */ -+ mode = kzalloc(i * sizeof(*mode), GFP_KERNEL); -+ if (mode) { -+ i = 0; -+ list_for_each(pos, &info->modelist) { -+ modelist = list_entry(pos, struct fb_modelist, list); -+ mode[i] = modelist->mode; -+ i++; -+ } -+ -+ if (!mode_option) -+ mode_option = UVESAFB_DEFAULT_MODE; -+ -+ i = fb_find_mode(&info->var, info, mode_option, mode, i, -+ NULL, 8); -+ -+ kfree(mode); -+ } -+ -+ /* fb_find_mode() failed */ -+ if (i == 0 || i >= 3) { -+ info->var.xres = 640; -+ info->var.yres = 480; -+ mode = (struct fb_videomode *) -+ fb_find_best_mode(&info->var, &info->modelist); -+ -+ if (mode) { -+ fb_videomode_to_var(&info->var, mode); -+ } else { -+ modeid = par->vbe_modes[0].mode_id; -+ fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, 60, -+ &info->var, info); -+ goto gotmode; -+ } -+ } -+ -+ /* Look for a matching VBE mode. */ -+ modeid = uvesafb_vbe_find_mode(par, info->var.xres, info->var.yres, -+ info->var.bits_per_pixel, UVESAFB_EXACT_RES); -+ -+ if (modeid == -1) -+ return -EINVAL; -+ -+gotmode: -+ uvesafb_setup_var(&info->var, info, &par->vbe_modes[modeid]); -+ -+ /* -+ * If we are not VBE3.0+ compliant, we're done -- the BIOS will -+ * ignore our timings anyway. -+ */ -+ if (par->vbe_ib.vbe_version < 0x0300 || par->nocrtc) -+ fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, 60, -+ &info->var, info); -+ -+ return modeid; -+} -+ -+static int uvesafb_setpalette(struct uvesafb_pal_entry *entries, int count, -+ int start, struct fb_info *info) -+{ -+ struct uvesafb_ktask *task; -+ struct uvesafb_par *par = info->par; -+ int i = par->mode_idx; -+ int err = 0; -+ -+ /* -+ * We support palette modifications for 8 bpp modes only, so -+ * there can never be more than 256 entries. -+ */ -+ if (start + count > 256) -+ return -EINVAL; -+ -+ /* Use VGA registers if mode is VGA-compatible. */ -+ if (i >= 0 && i < par->vbe_modes_cnt && -+ par->vbe_modes[i].mode_attr & VBE_MODE_VGACOMPAT) { -+ for (i = 0; i < count; i++) { -+ outb_p(start + i, dac_reg); -+ outb_p(entries[i].red, dac_val); -+ outb_p(entries[i].green, dac_val); -+ outb_p(entries[i].blue, dac_val); -+ } -+ } -+#ifdef CONFIG_X86_32 -+ else if (par->pmi_setpal) { -+ __asm__ __volatile__( -+ "call *(%%esi)" -+ : /* no return value */ -+ : "a" (0x4f09), /* EAX */ -+ "b" (0), /* EBX */ -+ "c" (count), /* ECX */ -+ "d" (start), /* EDX */ -+ "D" (entries), /* EDI */ -+ "S" (&par->pmi_pal)); /* ESI */ -+ } -+#endif -+ else { -+ task = uvesafb_prep(); -+ if (!task) -+ return -ENOMEM; -+ -+ task->t.regs.eax = 0x4f09; -+ task->t.regs.ebx = 0x0; -+ task->t.regs.ecx = count; -+ task->t.regs.edx = start; -+ task->t.flags = TF_BUF_ESDI; -+ task->t.buf_len = sizeof(struct uvesafb_pal_entry) * count; -+ task->buf = entries; -+ -+ err = uvesafb_exec(task); -+ if ((task->t.regs.eax & 0xffff) != 0x004f) -+ err = 1; -+ -+ uvesafb_free(task); -+ } -+ return err; -+} -+ -+static int uvesafb_setcolreg(unsigned regno, unsigned red, unsigned green, -+ unsigned blue, unsigned transp, -+ struct fb_info *info) -+{ -+ struct uvesafb_pal_entry entry; -+ int shift = 16 - info->var.green.length; -+ int err = 0; -+ -+ if (regno >= info->cmap.len) -+ return -EINVAL; -+ -+ if (info->var.bits_per_pixel == 8) { -+ entry.red = red >> shift; -+ entry.green = green >> shift; -+ entry.blue = blue >> shift; -+ entry.pad = 0; -+ -+ err = uvesafb_setpalette(&entry, 1, regno, info); -+ } else if (regno < 16) { -+ switch (info->var.bits_per_pixel) { -+ case 16: -+ if (info->var.red.offset == 10) { -+ /* 1:5:5:5 */ -+ ((u32 *) (info->pseudo_palette))[regno] = -+ ((red & 0xf800) >> 1) | -+ ((green & 0xf800) >> 6) | -+ ((blue & 0xf800) >> 11); -+ } else { -+ /* 0:5:6:5 */ -+ ((u32 *) (info->pseudo_palette))[regno] = -+ ((red & 0xf800) ) | -+ ((green & 0xfc00) >> 5) | -+ ((blue & 0xf800) >> 11); -+ } -+ break; -+ -+ case 24: -+ case 32: -+ red >>= 8; -+ green >>= 8; -+ blue >>= 8; -+ ((u32 *)(info->pseudo_palette))[regno] = -+ (red << info->var.red.offset) | -+ (green << info->var.green.offset) | -+ (blue << info->var.blue.offset); -+ break; -+ } -+ } -+ return err; -+} -+ -+static int uvesafb_setcmap(struct fb_cmap *cmap, struct fb_info *info) -+{ -+ struct uvesafb_pal_entry *entries; -+ int shift = 16 - info->var.green.length; -+ int i, err = 0; -+ -+ if (info->var.bits_per_pixel == 8) { -+ if (cmap->start + cmap->len > info->cmap.start + -+ info->cmap.len || cmap->start < info->cmap.start) -+ return -EINVAL; -+ -+ entries = kmalloc(sizeof(*entries) * cmap->len, GFP_KERNEL); -+ if (!entries) -+ return -ENOMEM; -+ -+ for (i = 0; i < cmap->len; i++) { -+ entries[i].red = cmap->red[i] >> shift; -+ entries[i].green = cmap->green[i] >> shift; -+ entries[i].blue = cmap->blue[i] >> shift; -+ entries[i].pad = 0; -+ } -+ err = uvesafb_setpalette(entries, cmap->len, cmap->start, info); -+ kfree(entries); -+ } else { -+ /* -+ * For modes with bpp > 8, we only set the pseudo palette in -+ * the fb_info struct. We rely on uvesafb_setcolreg to do all -+ * sanity checking. -+ */ -+ for (i = 0; i < cmap->len; i++) { -+ err |= uvesafb_setcolreg(cmap->start + i, cmap->red[i], -+ cmap->green[i], cmap->blue[i], -+ 0, info); -+ } -+ } -+ return err; -+} -+ -+static int uvesafb_pan_display(struct fb_var_screeninfo *var, -+ struct fb_info *info) -+{ -+#ifdef CONFIG_X86_32 -+ int offset; -+ struct uvesafb_par *par = info->par; -+ -+ offset = (var->yoffset * info->fix.line_length + var->xoffset) / 4; -+ -+ /* -+ * It turns out it's not the best idea to do panning via vm86, -+ * so we only allow it if we have a PMI. -+ */ -+ if (par->pmi_start) { -+ __asm__ __volatile__( -+ "call *(%%edi)" -+ : /* no return value */ -+ : "a" (0x4f07), /* EAX */ -+ "b" (0), /* EBX */ -+ "c" (offset), /* ECX */ -+ "d" (offset >> 16), /* EDX */ -+ "D" (&par->pmi_start)); /* EDI */ -+ } -+#endif -+ return 0; -+} -+ -+static int uvesafb_blank(int blank, struct fb_info *info) -+{ -+ struct uvesafb_par *par = info->par; -+ struct uvesafb_ktask *task; -+ int err = 1; -+ -+ if (par->vbe_ib.capabilities & VBE_CAP_VGACOMPAT) { -+ int loop = 10000; -+ u8 seq = 0, crtc17 = 0; -+ -+ if (blank == FB_BLANK_POWERDOWN) { -+ seq = 0x20; -+ crtc17 = 0x00; -+ err = 0; -+ } else { -+ seq = 0x00; -+ crtc17 = 0x80; -+ err = (blank == FB_BLANK_UNBLANK) ? 0 : -EINVAL; -+ } -+ -+ vga_wseq(NULL, 0x00, 0x01); -+ seq |= vga_rseq(NULL, 0x01) & ~0x20; -+ vga_wseq(NULL, 0x00, seq); -+ -+ crtc17 |= vga_rcrt(NULL, 0x17) & ~0x80; -+ while (loop--); -+ vga_wcrt(NULL, 0x17, crtc17); -+ vga_wseq(NULL, 0x00, 0x03); -+ } else { -+ task = uvesafb_prep(); -+ if (!task) -+ return -ENOMEM; -+ -+ task->t.regs.eax = 0x4f10; -+ switch (blank) { -+ case FB_BLANK_UNBLANK: -+ task->t.regs.ebx = 0x0001; -+ break; -+ case FB_BLANK_NORMAL: -+ task->t.regs.ebx = 0x0101; /* standby */ -+ break; -+ case FB_BLANK_POWERDOWN: -+ task->t.regs.ebx = 0x0401; /* powerdown */ -+ break; -+ default: -+ goto out; -+ } -+ -+ err = uvesafb_exec(task); -+ if (err || (task->t.regs.eax & 0xffff) != 0x004f) -+ err = 1; -+out: uvesafb_free(task); -+ } -+ return err; -+} -+ -+static int uvesafb_open(struct fb_info *info, int user) -+{ -+ struct uvesafb_par *par = info->par; -+ int cnt = atomic_read(&par->ref_count); -+ -+ if (!cnt && par->vbe_state_size) -+ par->vbe_state_orig = uvesafb_vbe_state_save(par); -+ -+ atomic_inc(&par->ref_count); -+ return 0; -+} -+ -+static int uvesafb_release(struct fb_info *info, int user) -+{ -+ struct uvesafb_ktask *task = NULL; -+ struct uvesafb_par *par = info->par; -+ int cnt = atomic_read(&par->ref_count); -+ -+ if (!cnt) -+ return -EINVAL; -+ -+ if (cnt != 1) -+ goto out; -+ -+ task = uvesafb_prep(); -+ if (!task) -+ goto out; -+ -+ /* First, try to set the standard 80x25 text mode. */ -+ task->t.regs.eax = 0x0003; -+ uvesafb_exec(task); -+ -+ /* -+ * Now try to restore whatever hardware state we might have -+ * saved when the fb device was first opened. -+ */ -+ uvesafb_vbe_state_restore(par, par->vbe_state_orig); -+out: -+ atomic_dec(&par->ref_count); -+ if (task) -+ uvesafb_free(task); -+ return 0; -+} -+ -+static int uvesafb_set_par(struct fb_info *info) -+{ -+ struct uvesafb_par *par = info->par; -+ struct uvesafb_ktask *task = NULL; -+ struct vbe_crtc_ib *crtc = NULL; -+ struct vbe_mode_ib *mode = NULL; -+ int i, err = 0, depth = info->var.bits_per_pixel; -+ -+ if (depth > 8 && depth != 32) -+ depth = info->var.red.length + info->var.green.length + -+ info->var.blue.length; -+ -+ i = uvesafb_vbe_find_mode(par, info->var.xres, info->var.yres, depth, -+ UVESAFB_EXACT_RES | UVESAFB_EXACT_DEPTH); -+ if (i >= 0) -+ mode = &par->vbe_modes[i]; -+ else -+ return -EINVAL; -+ -+ task = uvesafb_prep(); -+ if (!task) -+ return -ENOMEM; -+setmode: -+ task->t.regs.eax = 0x4f02; -+ task->t.regs.ebx = mode->mode_id | 0x4000; /* use LFB */ -+ -+ if (par->vbe_ib.vbe_version >= 0x0300 && !par->nocrtc && -+ info->var.pixclock != 0) { -+ task->t.regs.ebx |= 0x0800; /* use CRTC data */ -+ task->t.flags = TF_BUF_ESDI; -+ crtc = kzalloc(sizeof(struct vbe_crtc_ib), GFP_KERNEL); -+ if (!crtc) { -+ err = -ENOMEM; -+ goto out; -+ } -+ crtc->horiz_start = info->var.xres + info->var.right_margin; -+ crtc->horiz_end = crtc->horiz_start + info->var.hsync_len; -+ crtc->horiz_total = crtc->horiz_end + info->var.left_margin; -+ -+ crtc->vert_start = info->var.yres + info->var.lower_margin; -+ crtc->vert_end = crtc->vert_start + info->var.vsync_len; -+ crtc->vert_total = crtc->vert_end + info->var.upper_margin; -+ -+ crtc->pixel_clock = PICOS2KHZ(info->var.pixclock) * 1000; -+ crtc->refresh_rate = (u16)(100 * (crtc->pixel_clock / -+ (crtc->vert_total * crtc->horiz_total))); -+ -+ if (info->var.vmode & FB_VMODE_DOUBLE) -+ crtc->flags |= 0x1; -+ if (info->var.vmode & FB_VMODE_INTERLACED) -+ crtc->flags |= 0x2; -+ if (!(info->var.sync & FB_SYNC_HOR_HIGH_ACT)) -+ crtc->flags |= 0x4; -+ if (!(info->var.sync & FB_SYNC_VERT_HIGH_ACT)) -+ crtc->flags |= 0x8; -+ memcpy(&par->crtc, crtc, sizeof(*crtc)); -+ } else { -+ memset(&par->crtc, 0, sizeof(*crtc)); -+ } -+ -+ task->t.buf_len = sizeof(struct vbe_crtc_ib); -+ task->buf = &par->crtc; -+ -+ err = uvesafb_exec(task); -+ if (err || (task->t.regs.eax & 0xffff) != 0x004f) { -+ /* -+ * The mode switch might have failed because we tried to -+ * use our own timings. Try again with the default timings. -+ */ -+ if (crtc != NULL) { -+ printk(KERN_WARNING "uvesafb: mode switch failed " -+ "(eax=0x%x, err=%d). Trying again with " -+ "default timings.\n", task->t.regs.eax, err); -+ uvesafb_reset(task); -+ kfree(crtc); -+ crtc = NULL; -+ info->var.pixclock = 0; -+ goto setmode; -+ } else { -+ printk(KERN_ERR "uvesafb: mode switch failed (eax=" -+ "0x%x, err=%d)\n", task->t.regs.eax, err); -+ err = -EINVAL; -+ goto out; -+ } -+ } -+ par->mode_idx = i; -+ -+ /* For 8bpp modes, always try to set the DAC to 8 bits. */ -+ if (par->vbe_ib.capabilities & VBE_CAP_CAN_SWITCH_DAC && -+ mode->bits_per_pixel <= 8) { -+ uvesafb_reset(task); -+ task->t.regs.eax = 0x4f08; -+ task->t.regs.ebx = 0x0800; -+ -+ err = uvesafb_exec(task); -+ if (err || (task->t.regs.eax & 0xffff) != 0x004f || -+ ((task->t.regs.ebx & 0xff00) >> 8) != 8) { -+ /* -+ * We've failed to set the DAC palette format - -+ * time to correct var. -+ */ -+ info->var.red.length = 6; -+ info->var.green.length = 6; -+ info->var.blue.length = 6; -+ } -+ } -+ -+ info->fix.visual = (info->var.bits_per_pixel == 8) ? -+ FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; -+ info->fix.line_length = mode->bytes_per_scan_line; -+ -+out: if (crtc != NULL) -+ kfree(crtc); -+ uvesafb_free(task); -+ -+ return err; -+} -+ -+static void uvesafb_check_limits(struct fb_var_screeninfo *var, -+ struct fb_info *info) -+{ -+ const struct fb_videomode *mode; -+ struct uvesafb_par *par = info->par; -+ -+ /* -+ * If pixclock is set to 0, then we're using default BIOS timings -+ * and thus don't have to perform any checks here. -+ */ -+ if (!var->pixclock) -+ return; -+ -+ if (par->vbe_ib.vbe_version < 0x0300) { -+ fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, 60, var, info); -+ return; -+ } -+ -+ if (!fb_validate_mode(var, info)) -+ return; -+ -+ mode = fb_find_best_mode(var, &info->modelist); -+ if (mode) { -+ if (mode->xres == var->xres && mode->yres == var->yres && -+ !(mode->vmode & (FB_VMODE_INTERLACED | FB_VMODE_DOUBLE))) { -+ fb_videomode_to_var(var, mode); -+ return; -+ } -+ } -+ -+ if (info->monspecs.gtf && !fb_get_mode(FB_MAXTIMINGS, 0, var, info)) -+ return; -+ /* Use default refresh rate */ -+ var->pixclock = 0; -+} -+ -+static int uvesafb_check_var(struct fb_var_screeninfo *var, -+ struct fb_info *info) -+{ -+ struct uvesafb_par *par = info->par; -+ struct vbe_mode_ib *mode = NULL; -+ int match = -1; -+ int depth = var->red.length + var->green.length + var->blue.length; -+ -+ /* -+ * Various apps will use bits_per_pixel to set the color depth, -+ * which is theoretically incorrect, but which we'll try to handle -+ * here. -+ */ -+ if (depth == 0 || abs(depth - var->bits_per_pixel) >= 8) -+ depth = var->bits_per_pixel; -+ -+ match = uvesafb_vbe_find_mode(par, var->xres, var->yres, depth, -+ UVESAFB_EXACT_RES); -+ if (match == -1) -+ return -EINVAL; -+ -+ mode = &par->vbe_modes[match]; -+ uvesafb_setup_var(var, info, mode); -+ -+ /* -+ * Check whether we have remapped enough memory for this mode. -+ * We might be called at an early stage, when we haven't remapped -+ * any memory yet, in which case we simply skip the check. -+ */ -+ if (var->yres * mode->bytes_per_scan_line > info->fix.smem_len -+ && info->fix.smem_len) -+ return -EINVAL; -+ -+ if ((var->vmode & FB_VMODE_DOUBLE) && -+ !(par->vbe_modes[match].mode_attr & 0x100)) -+ var->vmode &= ~FB_VMODE_DOUBLE; -+ -+ if ((var->vmode & FB_VMODE_INTERLACED) && -+ !(par->vbe_modes[match].mode_attr & 0x200)) -+ var->vmode &= ~FB_VMODE_INTERLACED; -+ -+ uvesafb_check_limits(var, info); -+ -+ var->xres_virtual = var->xres; -+ var->yres_virtual = (par->ypan) ? -+ info->fix.smem_len / mode->bytes_per_scan_line : -+ var->yres; -+ return 0; -+} -+ -+static void uvesafb_save_state(struct fb_info *info) -+{ -+ struct uvesafb_par *par = info->par; -+ -+ if (par->vbe_state_saved) -+ kfree(par->vbe_state_saved); -+ -+ par->vbe_state_saved = uvesafb_vbe_state_save(par); -+} -+ -+static void uvesafb_restore_state(struct fb_info *info) -+{ -+ struct uvesafb_par *par = info->par; -+ -+ uvesafb_vbe_state_restore(par, par->vbe_state_saved); -+} -+ -+static struct fb_ops uvesafb_ops = { -+ .owner = THIS_MODULE, -+ .fb_open = uvesafb_open, -+ .fb_release = uvesafb_release, -+ .fb_setcolreg = uvesafb_setcolreg, -+ .fb_setcmap = uvesafb_setcmap, -+ .fb_pan_display = uvesafb_pan_display, -+ .fb_blank = uvesafb_blank, -+ .fb_fillrect = cfb_fillrect, -+ .fb_copyarea = cfb_copyarea, -+ .fb_imageblit = cfb_imageblit, -+ .fb_check_var = uvesafb_check_var, -+ .fb_set_par = uvesafb_set_par, -+ .fb_save_state = uvesafb_save_state, -+ .fb_restore_state = uvesafb_restore_state, -+}; -+ -+static void __devinit uvesafb_init_info(struct fb_info *info, -+ struct vbe_mode_ib *mode) -+{ -+ unsigned int size_vmode; -+ unsigned int size_remap; -+ unsigned int size_total; -+ struct uvesafb_par *par = info->par; -+ int i, h; -+ -+ info->pseudo_palette = ((u8 *)info->par + sizeof(struct uvesafb_par)); -+ info->fix = uvesafb_fix; -+ info->fix.ypanstep = par->ypan ? 1 : 0; -+ info->fix.ywrapstep = (par->ypan > 1) ? 1 : 0; -+ -+ /* -+ * If we were unable to get the state buffer size, disable -+ * functions for saving and restoring the hardware state. -+ */ -+ if (par->vbe_state_size == 0) { -+ info->fbops->fb_save_state = NULL; -+ info->fbops->fb_restore_state = NULL; -+ } -+ -+ /* Disable blanking if the user requested so. */ -+ if (!blank) -+ info->fbops->fb_blank = NULL; -+ -+ /* -+ * Find out how much IO memory is required for the mode with -+ * the highest resolution. -+ */ -+ size_remap = 0; -+ for (i = 0; i < par->vbe_modes_cnt; i++) { -+ h = par->vbe_modes[i].bytes_per_scan_line * -+ par->vbe_modes[i].y_res; -+ if (h > size_remap) -+ size_remap = h; -+ } -+ size_remap *= 2; -+ -+ /* -+ * size_vmode -- that is the amount of memory needed for the -+ * used video mode, i.e. the minimum amount of -+ * memory we need. -+ */ -+ if (mode != NULL) { -+ size_vmode = info->var.yres * mode->bytes_per_scan_line; -+ } else { -+ size_vmode = info->var.yres * info->var.xres * -+ ((info->var.bits_per_pixel + 7) >> 3); -+ } -+ -+ /* -+ * size_total -- all video memory we have. Used for mtrr -+ * entries, resource allocation and bounds -+ * checking. -+ */ -+ size_total = par->vbe_ib.total_memory * 65536; -+ if (vram_total) -+ size_total = vram_total * 1024 * 1024; -+ if (size_total < size_vmode) -+ size_total = size_vmode; -+ -+ /* -+ * size_remap -- the amount of video memory we are going to -+ * use for vesafb. With modern cards it is no -+ * option to simply use size_total as th -+ * wastes plenty of kernel address space. -+ */ -+ if (vram_remap) -+ size_remap = vram_remap * 1024 * 1024; -+ if (size_remap < size_vmode) -+ size_remap = size_vmode; -+ if (size_remap > size_total) -+ size_remap = size_total; -+ -+ info->fix.smem_len = size_remap; -+ info->fix.smem_start = mode->phys_base_ptr; -+ -+ /* -+ * We have to set yres_virtual here because when setup_var() was -+ * called, smem_len wasn't defined yet. -+ */ -+ info->var.yres_virtual = info->fix.smem_len / -+ mode->bytes_per_scan_line; -+ -+ if (par->ypan && info->var.yres_virtual > info->var.yres) { -+ printk(KERN_INFO "uvesafb: scrolling: %s " -+ "using protected mode interface, " -+ "yres_virtual=%d\n", -+ (par->ypan > 1) ? "ywrap" : "ypan", -+ info->var.yres_virtual); -+ } else { -+ printk(KERN_INFO "uvesafb: scrolling: redraw\n"); -+ info->var.yres_virtual = info->var.yres; -+ par->ypan = 0; -+ } -+ -+ info->flags = FBINFO_FLAG_DEFAULT | -+ (par->ypan) ? FBINFO_HWACCEL_YPAN : 0; -+ -+ if (!par->ypan) -+ info->fbops->fb_pan_display = NULL; -+} -+ -+static void uvesafb_init_mtrr(struct fb_info *info) -+{ -+#ifdef CONFIG_MTRR -+ if (mtrr && !(info->fix.smem_start & (PAGE_SIZE - 1))) { -+ int temp_size = info->fix.smem_len; -+ unsigned int type = 0; -+ -+ switch (mtrr) { -+ case 1: -+ type = MTRR_TYPE_UNCACHABLE; -+ break; -+ case 2: -+ type = MTRR_TYPE_WRBACK; -+ break; -+ case 3: -+ type = MTRR_TYPE_WRCOMB; -+ break; -+ case 4: -+ type = MTRR_TYPE_WRTHROUGH; -+ break; -+ default: -+ type = 0; -+ break; -+ } -+ -+ if (type) { -+ int rc; -+ -+ /* Find the largest power-of-two */ -+ while (temp_size & (temp_size - 1)) -+ temp_size &= (temp_size - 1); -+ -+ /* Try and find a power of two to add */ -+ do { -+ rc = mtrr_add(info->fix.smem_start, -+ temp_size, type, 1); -+ temp_size >>= 1; -+ } while (temp_size >= PAGE_SIZE && rc == -EINVAL); -+ } -+ } -+#endif /* CONFIG_MTRR */ -+} -+ -+ -+static ssize_t uvesafb_show_vbe_ver(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct fb_info *info = platform_get_drvdata(to_platform_device(dev)); -+ struct uvesafb_par *par = info->par; -+ -+ return snprintf(buf, PAGE_SIZE, "%.4x\n", par->vbe_ib.vbe_version); -+} -+ -+static DEVICE_ATTR(vbe_version, S_IRUGO, uvesafb_show_vbe_ver, NULL); -+ -+static ssize_t uvesafb_show_vbe_modes(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct fb_info *info = platform_get_drvdata(to_platform_device(dev)); -+ struct uvesafb_par *par = info->par; -+ int ret = 0, i; -+ -+ for (i = 0; i < par->vbe_modes_cnt && ret < PAGE_SIZE; i++) { -+ ret += snprintf(buf + ret, PAGE_SIZE - ret, -+ "%dx%d-%d, 0x%.4x\n", -+ par->vbe_modes[i].x_res, par->vbe_modes[i].y_res, -+ par->vbe_modes[i].depth, par->vbe_modes[i].mode_id); -+ } -+ -+ return ret; -+} -+ -+static DEVICE_ATTR(vbe_modes, S_IRUGO, uvesafb_show_vbe_modes, NULL); -+ -+static ssize_t uvesafb_show_vendor(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct fb_info *info = platform_get_drvdata(to_platform_device(dev)); -+ struct uvesafb_par *par = info->par; -+ -+ if (par->vbe_ib.oem_vendor_name_ptr) -+ return snprintf(buf, PAGE_SIZE, "%s\n", (char *) -+ (&par->vbe_ib) + par->vbe_ib.oem_vendor_name_ptr); -+ else -+ return 0; -+} -+ -+static DEVICE_ATTR(oem_vendor, S_IRUGO, uvesafb_show_vendor, NULL); -+ -+static ssize_t uvesafb_show_product_name(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct fb_info *info = platform_get_drvdata(to_platform_device(dev)); -+ struct uvesafb_par *par = info->par; -+ -+ if (par->vbe_ib.oem_product_name_ptr) -+ return snprintf(buf, PAGE_SIZE, "%s\n", (char *) -+ (&par->vbe_ib) + par->vbe_ib.oem_product_name_ptr); -+ else -+ return 0; -+} -+ -+static DEVICE_ATTR(oem_product_name, S_IRUGO, uvesafb_show_product_name, NULL); -+ -+static ssize_t uvesafb_show_product_rev(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct fb_info *info = platform_get_drvdata(to_platform_device(dev)); -+ struct uvesafb_par *par = info->par; -+ -+ if (par->vbe_ib.oem_product_rev_ptr) -+ return snprintf(buf, PAGE_SIZE, "%s\n", (char *) -+ (&par->vbe_ib) + par->vbe_ib.oem_product_rev_ptr); -+ else -+ return 0; -+} -+ -+static DEVICE_ATTR(oem_product_rev, S_IRUGO, uvesafb_show_product_rev, NULL); -+ -+static ssize_t uvesafb_show_oem_string(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct fb_info *info = platform_get_drvdata(to_platform_device(dev)); -+ struct uvesafb_par *par = info->par; -+ -+ if (par->vbe_ib.oem_string_ptr) -+ return snprintf(buf, PAGE_SIZE, "%s\n", -+ (char *)(&par->vbe_ib) + par->vbe_ib.oem_string_ptr); -+ else -+ return 0; -+} -+ -+static DEVICE_ATTR(oem_string, S_IRUGO, uvesafb_show_oem_string, NULL); -+ -+static ssize_t uvesafb_show_nocrtc(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct fb_info *info = platform_get_drvdata(to_platform_device(dev)); -+ struct uvesafb_par *par = info->par; -+ -+ return snprintf(buf, PAGE_SIZE, "%d\n", par->nocrtc); -+} -+ -+static ssize_t uvesafb_store_nocrtc(struct device *dev, -+ struct device_attribute *attr, const char *buf, size_t count) -+{ -+ struct fb_info *info = platform_get_drvdata(to_platform_device(dev)); -+ struct uvesafb_par *par = info->par; -+ -+ if (count > 0) { -+ if (buf[0] == '0') -+ par->nocrtc = 0; -+ else -+ par->nocrtc = 1; -+ } -+ return count; -+} -+ -+static DEVICE_ATTR(nocrtc, S_IRUGO | S_IWUSR, uvesafb_show_nocrtc, -+ uvesafb_store_nocrtc); -+ -+static struct attribute *uvesafb_dev_attrs[] = { -+ &dev_attr_vbe_version.attr, -+ &dev_attr_vbe_modes.attr, -+ &dev_attr_oem_vendor.attr, -+ &dev_attr_oem_product_name.attr, -+ &dev_attr_oem_product_rev.attr, -+ &dev_attr_oem_string.attr, -+ &dev_attr_nocrtc.attr, -+ NULL, -+}; -+ -+static struct attribute_group uvesafb_dev_attgrp = { -+ .name = NULL, -+ .attrs = uvesafb_dev_attrs, -+}; -+ -+static int __devinit uvesafb_probe(struct platform_device *dev) -+{ -+ struct fb_info *info; -+ struct vbe_mode_ib *mode = NULL; -+ struct uvesafb_par *par; -+ int err = 0, i; -+ -+ info = framebuffer_alloc(sizeof(*par) + sizeof(u32) * 256, &dev->dev); -+ if (!info) -+ return -ENOMEM; -+ -+ par = info->par; -+ -+ err = uvesafb_vbe_init(info); -+ if (err) { -+ printk(KERN_ERR "uvesafb: vbe_init() failed with %d\n", err); -+ goto out; -+ } -+ -+ info->fbops = &uvesafb_ops; -+ -+ i = uvesafb_vbe_init_mode(info); -+ if (i < 0) { -+ err = -EINVAL; -+ goto out; -+ } else { -+ mode = &par->vbe_modes[i]; -+ } -+ -+ if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) { -+ err = -ENXIO; -+ goto out; -+ } -+ -+ uvesafb_init_info(info, mode); -+ -+ if (!request_mem_region(info->fix.smem_start, info->fix.smem_len, -+ "uvesafb")) { -+ printk(KERN_ERR "uvesafb: cannot reserve video memory at " -+ "0x%lx\n", info->fix.smem_start); -+ err = -EIO; -+ goto out_mode; -+ } -+ -+ info->screen_base = ioremap(info->fix.smem_start, info->fix.smem_len); -+ -+ if (!info->screen_base) { -+ printk(KERN_ERR -+ "uvesafb: abort, cannot ioremap 0x%x bytes of video " -+ "memory at 0x%lx\n", -+ info->fix.smem_len, info->fix.smem_start); -+ err = -EIO; -+ goto out_mem; -+ } -+ -+ if (!request_region(0x3c0, 32, "uvesafb")) { -+ printk(KERN_ERR "uvesafb: request region 0x3c0-0x3e0 failed\n"); -+ err = -EIO; -+ goto out_unmap; -+ } -+ -+ uvesafb_init_mtrr(info); -+ platform_set_drvdata(dev, info); -+ -+ if (register_framebuffer(info) < 0) { -+ printk(KERN_ERR -+ "uvesafb: failed to register framebuffer device\n"); -+ err = -EINVAL; -+ goto out_reg; -+ } -+ -+ printk(KERN_INFO "uvesafb: framebuffer at 0x%lx, mapped to 0x%p, " -+ "using %dk, total %dk\n", info->fix.smem_start, -+ info->screen_base, info->fix.smem_len/1024, -+ par->vbe_ib.total_memory * 64); -+ printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, -+ info->fix.id); -+ -+ err = sysfs_create_group(&dev->dev.kobj, &uvesafb_dev_attgrp); -+ if (err != 0) -+ printk(KERN_WARNING "fb%d: failed to register attributes\n", -+ info->node); -+ -+ return 0; -+ -+out_reg: -+ release_region(0x3c0, 32); -+out_unmap: -+ iounmap(info->screen_base); -+out_mem: -+ release_mem_region(info->fix.smem_start, info->fix.smem_len); -+out_mode: -+ if (!list_empty(&info->modelist)) -+ fb_destroy_modelist(&info->modelist); -+ fb_destroy_modedb(info->monspecs.modedb); -+ fb_dealloc_cmap(&info->cmap); -+out: -+ if (par->vbe_modes) -+ kfree(par->vbe_modes); -+ -+ framebuffer_release(info); -+ return err; -+} -+ -+static int uvesafb_remove(struct platform_device *dev) -+{ -+ struct fb_info *info = platform_get_drvdata(dev); -+ -+ if (info) { -+ struct uvesafb_par *par = info->par; -+ -+ sysfs_remove_group(&dev->dev.kobj, &uvesafb_dev_attgrp); -+ unregister_framebuffer(info); -+ release_region(0x3c0, 32); -+ iounmap(info->screen_base); -+ release_mem_region(info->fix.smem_start, info->fix.smem_len); -+ fb_destroy_modedb(info->monspecs.modedb); -+ fb_dealloc_cmap(&info->cmap); -+ -+ if (par) { -+ if (par->vbe_modes) -+ kfree(par->vbe_modes); -+ if (par->vbe_state_orig) -+ kfree(par->vbe_state_orig); -+ if (par->vbe_state_saved) -+ kfree(par->vbe_state_saved); -+ } -+ -+ framebuffer_release(info); -+ } -+ return 0; -+} -+ -+static struct platform_driver uvesafb_driver = { -+ .probe = uvesafb_probe, -+ .remove = uvesafb_remove, -+ .driver = { -+ .name = "uvesafb", -+ }, -+}; -+ -+static struct platform_device *uvesafb_device; -+ -+#ifndef MODULE -+static int __devinit uvesafb_setup(char *options) -+{ -+ char *this_opt; -+ -+ if (!options || !*options) -+ return 0; -+ -+ while ((this_opt = strsep(&options, ",")) != NULL) { -+ if (!*this_opt) continue; -+ -+ if (!strcmp(this_opt, "redraw")) -+ ypan = 0; -+ else if (!strcmp(this_opt, "ypan")) -+ ypan = 1; -+ else if (!strcmp(this_opt, "ywrap")) -+ ypan = 2; -+ else if (!strcmp(this_opt, "vgapal")) -+ pmi_setpal = 0; -+ else if (!strcmp(this_opt, "pmipal")) -+ pmi_setpal = 1; -+ else if (!strncmp(this_opt, "mtrr:", 5)) -+ mtrr = simple_strtoul(this_opt+5, NULL, 0); -+ else if (!strcmp(this_opt, "nomtrr")) -+ mtrr = 0; -+ else if (!strcmp(this_opt, "nocrtc")) -+ nocrtc = 1; -+ else if (!strcmp(this_opt, "noedid")) -+ noedid = 1; -+ else if (!strcmp(this_opt, "noblank")) -+ blank = 0; -+ else if (!strncmp(this_opt, "vtotal:", 7)) -+ vram_total = simple_strtoul(this_opt + 7, NULL, 0); -+ else if (!strncmp(this_opt, "vremap:", 7)) -+ vram_remap = simple_strtoul(this_opt + 7, NULL, 0); -+ else if (!strncmp(this_opt, "maxhf:", 6)) -+ maxhf = simple_strtoul(this_opt + 6, NULL, 0); -+ else if (!strncmp(this_opt, "maxvf:", 6)) -+ maxvf = simple_strtoul(this_opt + 6, NULL, 0); -+ else if (!strncmp(this_opt, "maxclk:", 7)) -+ maxclk = simple_strtoul(this_opt + 7, NULL, 0); -+ else if (!strncmp(this_opt, "vbemode:", 8)) -+ vbemode = simple_strtoul(this_opt + 8, NULL, 0); -+ else if (this_opt[0] >= '0' && this_opt[0] <= '9') { -+ mode_option = this_opt; -+ } else { -+ printk(KERN_WARNING -+ "uvesafb: unrecognized option %s\n", this_opt); -+ } -+ } -+ -+ return 0; -+} -+#endif /* !MODULE */ -+ -+static ssize_t show_v86d(struct device_driver *dev, char *buf) -+{ -+ return snprintf(buf, PAGE_SIZE, "%s\n", v86d_path); -+} -+ -+static ssize_t store_v86d(struct device_driver *dev, const char *buf, -+ size_t count) -+{ -+ strncpy(v86d_path, buf, PATH_MAX); -+ return count; -+} -+ -+static DRIVER_ATTR(v86d, S_IRUGO | S_IWUSR, show_v86d, store_v86d); -+ -+static int __devinit uvesafb_init(void) -+{ -+ int err; -+ -+#ifndef MODULE -+ char *option = NULL; -+ -+ if (fb_get_options("uvesafb", &option)) -+ return -ENODEV; -+ uvesafb_setup(option); -+#endif -+ err = cn_add_callback(&uvesafb_cn_id, "uvesafb", uvesafb_cn_callback); -+ if (err) -+ return err; -+ -+ err = platform_driver_register(&uvesafb_driver); -+ -+ if (!err) { -+ uvesafb_device = platform_device_alloc("uvesafb", 0); -+ if (uvesafb_device) -+ err = platform_device_add(uvesafb_device); -+ else -+ err = -ENOMEM; -+ -+ if (err) { -+ platform_device_put(uvesafb_device); -+ platform_driver_unregister(&uvesafb_driver); -+ cn_del_callback(&uvesafb_cn_id); -+ return err; -+ } -+ -+ err = driver_create_file(&uvesafb_driver.driver, -+ &driver_attr_v86d); -+ if (err) { -+ printk(KERN_WARNING "uvesafb: failed to register " -+ "attributes\n"); -+ err = 0; -+ } -+ } -+ return err; -+} -+ -+module_init(uvesafb_init); -+ -+static void __devexit uvesafb_exit(void) -+{ -+ struct uvesafb_ktask *task; -+ -+ if (v86d_started) { -+ task = uvesafb_prep(); -+ if (task) { -+ task->t.flags = TF_EXIT; -+ uvesafb_exec(task); -+ uvesafb_free(task); -+ } -+ } -+ -+ cn_del_callback(&uvesafb_cn_id); -+ driver_remove_file(&uvesafb_driver.driver, &driver_attr_v86d); -+ platform_device_unregister(uvesafb_device); -+ platform_driver_unregister(&uvesafb_driver); -+} -+ -+module_exit(uvesafb_exit); -+ -+static inline int param_get_scroll(char *buffer, struct kernel_param *kp) -+{ -+ return 0; -+} -+ -+static inline int param_set_scroll(const char *val, struct kernel_param *kp) -+{ -+ ypan = 0; -+ -+ if (!strcmp(val, "redraw")) -+ ypan = 0; -+ else if (!strcmp(val, "ypan")) -+ ypan = 1; -+ else if (!strcmp(val, "ywrap")) -+ ypan = 2; -+ -+ return 0; -+} -+ -+#define param_check_scroll(name, p) __param_check(name, p, void); -+ -+module_param_named(scroll, ypan, scroll, 0); -+MODULE_PARM_DESC(scroll, -+ "Scrolling mode, set to 'redraw', ''ypan' or 'ywrap'"); -+module_param_named(vgapal, pmi_setpal, invbool, 0); -+MODULE_PARM_DESC(vgapal, "Set palette using VGA registers"); -+module_param_named(pmipal, pmi_setpal, bool, 0); -+MODULE_PARM_DESC(pmipal, "Set palette using PMI calls"); -+module_param(mtrr, uint, 0); -+MODULE_PARM_DESC(mtrr, -+ "Memory Type Range Registers setting. Use 0 to disable."); -+module_param(blank, bool, 0); -+MODULE_PARM_DESC(blank, "Enable hardware blanking"); -+module_param(nocrtc, bool, 0); -+MODULE_PARM_DESC(nocrtc, "Ignore CRTC timings when setting modes"); -+module_param(noedid, bool, 0); -+MODULE_PARM_DESC(noedid, -+ "Ignore EDID-provided monitor limits when setting modes"); -+module_param(vram_remap, uint, 0); -+MODULE_PARM_DESC(vram_remap, "Set amount of video memory to be used [MiB]"); -+module_param(vram_total, uint, 0); -+MODULE_PARM_DESC(vram_total, "Set total amount of video memoery [MiB]"); -+module_param(maxclk, ushort, 0); -+MODULE_PARM_DESC(maxclk, "Maximum pixelclock [MHz], overrides EDID data"); -+module_param(maxhf, ushort, 0); -+MODULE_PARM_DESC(maxhf, -+ "Maximum horizontal frequency [kHz], overrides EDID data"); -+module_param(maxvf, ushort, 0); -+MODULE_PARM_DESC(maxvf, -+ "Maximum vertical frequency [Hz], overrides EDID data"); -+module_param_named(mode, mode_option, charp, 0); -+MODULE_PARM_DESC(mode, -+ "Specify initial video mode as \"<xres>x<yres>[-<bpp>][@<refresh>]\""); -+module_param(vbemode, ushort, 0); -+MODULE_PARM_DESC(vbemode, -+ "VBE mode number to set, overrides the 'mode' option"); -+module_param_string(v86d, v86d_path, PATH_MAX, 0660); -+MODULE_PARM_DESC(v86d, "Path to the v86d userspace helper."); -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Michal Januszewski <spock@gentoo.org>"); -+MODULE_DESCRIPTION("Framebuffer driver for VBE2.0+ compliant graphics boards"); -+ -Index: linux-2.6.22/include/linux/connector.h -=================================================================== ---- linux-2.6.22.orig/include/linux/connector.h 2007-08-28 21:54:13.000000000 +0100 -+++ linux-2.6.22/include/linux/connector.h 2007-08-28 21:56:34.000000000 +0100 -@@ -36,14 +36,15 @@ - #define CN_VAL_CIFS 0x1 - #define CN_W1_IDX 0x3 /* w1 communication */ - #define CN_W1_VAL 0x1 -+#define CN_IDX_V86D 0x4 -+#define CN_VAL_V86D_UVESAFB 0x1 - -- --#define CN_NETLINK_USERS 4 -+#define CN_NETLINK_USERS 5 - - /* - * Maximum connector's message size. - */ --#define CONNECTOR_MAX_MSG_SIZE 1024 -+#define CONNECTOR_MAX_MSG_SIZE 16384 - - /* - * idx and val are unique identifiers which -Index: linux-2.6.22/include/video/Kbuild -=================================================================== ---- linux-2.6.22.orig/include/video/Kbuild 2007-08-28 21:54:13.000000000 +0100 -+++ linux-2.6.22/include/video/Kbuild 2007-08-28 21:56:34.000000000 +0100 -@@ -1 +1 @@ --unifdef-y += sisfb.h -+unifdef-y += sisfb.h uvesafb.h -Index: linux-2.6.22/include/video/uvesafb.h -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/include/video/uvesafb.h 2007-08-28 21:56:34.000000000 +0100 -@@ -0,0 +1,193 @@ -+#ifndef _UVESAFB_H -+#define _UVESAFB_H -+ -+struct v86_regs { -+ __u32 ebx; -+ __u32 ecx; -+ __u32 edx; -+ __u32 esi; -+ __u32 edi; -+ __u32 ebp; -+ __u32 eax; -+ __u32 eip; -+ __u32 eflags; -+ __u32 esp; -+ __u16 cs; -+ __u16 ss; -+ __u16 es; -+ __u16 ds; -+ __u16 fs; -+ __u16 gs; -+}; -+ -+/* Task flags */ -+#define TF_VBEIB 0x01 -+#define TF_BUF_ESDI 0x02 -+#define TF_BUF_ESBX 0x04 -+#define TF_BUF_RET 0x08 -+#define TF_EXIT 0x10 -+ -+struct uvesafb_task { -+ __u8 flags; -+ int buf_len; -+ struct v86_regs regs; -+}; -+ -+/* Constants for the capabilities field -+ * in vbe_ib */ -+#define VBE_CAP_CAN_SWITCH_DAC 0x01 -+#define VBE_CAP_VGACOMPAT 0x02 -+ -+/* The VBE Info Block */ -+struct vbe_ib { -+ char vbe_signature[4]; -+ __u16 vbe_version; -+ __u32 oem_string_ptr; -+ __u32 capabilities; -+ __u32 mode_list_ptr; -+ __u16 total_memory; -+ __u16 oem_software_rev; -+ __u32 oem_vendor_name_ptr; -+ __u32 oem_product_name_ptr; -+ __u32 oem_product_rev_ptr; -+ __u8 reserved[222]; -+ char oem_data[256]; -+ char misc_data[512]; -+} __attribute__ ((packed)); -+ -+#ifdef __KERNEL__ -+ -+/* VBE CRTC Info Block */ -+struct vbe_crtc_ib { -+ u16 horiz_total; -+ u16 horiz_start; -+ u16 horiz_end; -+ u16 vert_total; -+ u16 vert_start; -+ u16 vert_end; -+ u8 flags; -+ u32 pixel_clock; -+ u16 refresh_rate; -+ u8 reserved[40]; -+} __attribute__ ((packed)); -+ -+#define VBE_MODE_VGACOMPAT 0x20 -+#define VBE_MODE_COLOR 0x08 -+#define VBE_MODE_SUPPORTEDHW 0x01 -+#define VBE_MODE_GRAPHICS 0x10 -+#define VBE_MODE_LFB 0x80 -+ -+#define VBE_MODE_MASK (VBE_MODE_COLOR | VBE_MODE_SUPPORTEDHW | \ -+ VBE_MODE_GRAPHICS | VBE_MODE_LFB) -+ -+/* VBE Mode Info Block */ -+struct vbe_mode_ib { -+ /* for all VBE revisions */ -+ u16 mode_attr; -+ u8 winA_attr; -+ u8 winB_attr; -+ u16 win_granularity; -+ u16 win_size; -+ u16 winA_seg; -+ u16 winB_seg; -+ u32 win_func_ptr; -+ u16 bytes_per_scan_line; -+ -+ /* for VBE 1.2+ */ -+ u16 x_res; -+ u16 y_res; -+ u8 x_char_size; -+ u8 y_char_size; -+ u8 planes; -+ u8 bits_per_pixel; -+ u8 banks; -+ u8 memory_model; -+ u8 bank_size; -+ u8 image_pages; -+ u8 reserved1; -+ -+ /* Direct color fields for direct/6 and YUV/7 memory models. */ -+ /* Offsets are bit positions of lsb in the mask. */ -+ u8 red_len; -+ u8 red_off; -+ u8 green_len; -+ u8 green_off; -+ u8 blue_len; -+ u8 blue_off; -+ u8 rsvd_len; -+ u8 rsvd_off; -+ u8 direct_color_info; /* direct color mode attributes */ -+ -+ /* for VBE 2.0+ */ -+ u32 phys_base_ptr; -+ u8 reserved2[6]; -+ -+ /* for VBE 3.0+ */ -+ u16 lin_bytes_per_scan_line; -+ u8 bnk_image_pages; -+ u8 lin_image_pages; -+ u8 lin_red_len; -+ u8 lin_red_off; -+ u8 lin_green_len; -+ u8 lin_green_off; -+ u8 lin_blue_len; -+ u8 lin_blue_off; -+ u8 lin_rsvd_len; -+ u8 lin_rsvd_off; -+ u32 max_pixel_clock; -+ u16 mode_id; -+ u8 depth; -+} __attribute__ ((packed)); -+ -+#define UVESAFB_DEFAULT_MODE "640x480-16" -+ -+/* How long to wait for a reply from userspace [ms] */ -+#define UVESAFB_TIMEOUT 5000 -+ -+/* Max number of concurrent tasks */ -+#define UVESAFB_TASKS_MAX 16 -+ -+#define dac_reg (0x3c8) -+#define dac_val (0x3c9) -+ -+struct uvesafb_pal_entry { -+ u_char blue, green, red, pad; -+} __attribute__ ((packed)); -+ -+struct uvesafb_ktask { -+ struct uvesafb_task t; -+ void *buf; -+ struct completion *done; -+ u32 ack; -+}; -+ -+static int uvesafb_exec(struct uvesafb_ktask *tsk); -+ -+#define UVESAFB_EXACT_RES 1 -+#define UVESAFB_EXACT_DEPTH 2 -+ -+struct uvesafb_par { -+ struct vbe_ib vbe_ib; /* VBE Info Block */ -+ struct vbe_mode_ib *vbe_modes; /* list of supported VBE modes */ -+ int vbe_modes_cnt; -+ -+ u8 nocrtc; -+ u8 ypan; /* 0 - nothing, 1 - ypan, 2 - ywrap */ -+ u8 pmi_setpal; /* PMI for palette changes */ -+ u16 *pmi_base; /* protected mode interface location */ -+ void *pmi_start; -+ void *pmi_pal; -+ u8 *vbe_state_orig; /* -+ * original hardware state, before the -+ * driver was loaded -+ */ -+ u8 *vbe_state_saved; /* state saved by fb_save_state */ -+ int vbe_state_size; -+ atomic_t ref_count; -+ -+ int mode_idx; -+ struct vbe_crtc_ib crtc; -+}; -+ -+#endif /* __KERNEL__ */ -+#endif /* _UVESAFB_H */ diff --git a/meta/packages/linux/linux-rp-2.6.23+2.6.24-rc8/vt_ioctl_race.patch b/meta/packages/linux/linux-rp-2.6.23+2.6.24-rc8/vt_ioctl_race.patch deleted file mode 100644 index 5a51d1c3f5..0000000000 --- a/meta/packages/linux/linux-rp-2.6.23+2.6.24-rc8/vt_ioctl_race.patch +++ /dev/null @@ -1,46 +0,0 @@ ---- - drivers/char/vt_ioctl.c | 8 +++++--- - 1 file changed, 5 insertions(+), 3 deletions(-) - -Index: linux-2.6.22/drivers/char/vt_ioctl.c -=================================================================== ---- linux-2.6.22.orig/drivers/char/vt_ioctl.c 2007-07-09 01:32:17.000000000 +0200 -+++ linux-2.6.22/drivers/char/vt_ioctl.c 2007-09-27 11:58:42.000000000 +0200 -@@ -770,6 +770,7 @@ - /* - * Switching-from response - */ -+ acquire_console_sem(); - if (vc->vt_newvt >= 0) { - if (arg == 0) - /* -@@ -784,7 +785,6 @@ - * complete the switch. - */ - int newvt; -- acquire_console_sem(); - newvt = vc->vt_newvt; - vc->vt_newvt = -1; - i = vc_allocate(newvt); -@@ -798,7 +798,6 @@ - * other console switches.. - */ - complete_change_console(vc_cons[newvt].d); -- release_console_sem(); - } - } - -@@ -810,9 +809,12 @@ - /* - * If it's just an ACK, ignore it - */ -- if (arg != VT_ACKACQ) -+ if (arg != VT_ACKACQ) { -+ release_console_sem(); - return -EINVAL; -+ } - } -+ release_console_sem(); - - return 0; - diff --git a/meta/packages/linux/linux-rp-2.6.23+2.6.24-rc8/w100fb-unused-var.patch b/meta/packages/linux/linux-rp-2.6.23+2.6.24-rc8/w100fb-unused-var.patch deleted file mode 100644 index 8cbbb6bd01..0000000000 --- a/meta/packages/linux/linux-rp-2.6.23+2.6.24-rc8/w100fb-unused-var.patch +++ /dev/null @@ -1,17 +0,0 @@ -From: Marcin Juszkiewicz <openembedded@haerwu.biz> - -drivers/video/w100fb.c: In function ‘w100fb_imageblit’: -drivers/video/w100fb.c:507: warning: unused variable ‘par’ - -Signed-off-by: Marcin Juszkiewicz <openembedded@haerwu.biz> - ---- linux-2.6.23/drivers/video/w100fb.c 2007-10-11 16:52:30.000000000 +0200 -+++ linux-2.6.23/drivers/video/w100fb.c 2007-10-15 12:56:01.000000000 +0200 -@@ -504,7 +504,6 @@ static void w100_hostdata(u32 width, u32 - static void w100fb_imageblit(struct fb_info *info, - const struct fb_image *image) - { -- struct w100fb_par *par = info->par; - union dp_gui_master_cntl_u gmc; - u32 fgcolor, bgcolor; - diff --git a/meta/packages/linux/linux-rp-2.6.23+2.6.24-rc8/wm97xx-lcdnoise-r0.patch b/meta/packages/linux/linux-rp-2.6.23+2.6.24-rc8/wm97xx-lcdnoise-r0.patch deleted file mode 100644 index 191de3af22..0000000000 --- a/meta/packages/linux/linux-rp-2.6.23+2.6.24-rc8/wm97xx-lcdnoise-r0.patch +++ /dev/null @@ -1,208 +0,0 @@ -Index: linux-tosa/drivers/input/touchscreen/wm9712.c -=================================================================== ---- linux-tosa.orig/drivers/input/touchscreen/wm9712.c 2006-08-29 16:52:36.008543280 +0100 -+++ linux-tosa/drivers/input/touchscreen/wm9712.c 2006-08-29 16:52:50.923275896 +0100 -@@ -1,7 +1,7 @@ - /* - * wm9712.c -- Codec driver for Wolfson WM9712 AC97 Codecs. - * -- * Copyright 2003, 2004, 2005 Wolfson Microelectronics PLC. -+ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC. - * Author: Liam Girdwood - * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com - * Parts Copyright : Ian Molton <spyro@f2s.com> -@@ -13,6 +13,12 @@ - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * -+ * Revision history -+ * 4th Jul 2005 Initial version. -+ * 29th Aug 2006 Mike Arthur <mike@mikearthur.co.uk> -+ * Added fixes for Sharp SL-6000 (Tosa) LCD noise causing -+ * touchscreen interference. -+ * - */ - - #include <linux/module.h> -@@ -28,6 +34,10 @@ - #define WM9705_VERSION "0.60" - #define DEFAULT_PRESSURE 0xb0c0 - -+#define CCNT(a) asm volatile ("mrc p14, 0, %0, C1, C1, 0" : "=r"(a)) -+#define CCNT_ON() asm("mcr p14, 0, %0, C0, C0, 0" : : "r"(1)) -+#define CCNT_OFF() asm("mcr p14, 0, %0, C0, C0, 0" : : "r"(1)) -+ - /* - * Debug - */ -@@ -243,6 +253,36 @@ - return wm->dig[2] & WM9712_PDEN; - } - -+ -+#ifdef CONFIG_MACH_TOSA -+/* On the Sharp SL-6000 (Tosa), due to a noisy LCD, we need to perform a wait -+ * before sampling the Y axis of the touchscreen */ -+static inline void wm9712_lcd_sync_on(struct wm97xx* wm, int adcsel) { -+ unsigned long timer1 = 0, timer2 = 0, wait_time = 0; -+ if (adcsel == WM97XX_ADCSEL_Y) { -+ wait_time = wm97xx_calc_lcd_waittime(wm); -+ -+ CCNT_ON(); -+ -+ if (wait_time) { -+ /* wait for LCD rising edge */ -+ wm_machinfo->wait_hsync(); -+ /* get clock */ -+ CCNT(timer1); -+ CCNT(timer2); -+ -+ while ((timer2 - timer1) < wait_time) { -+ CCNT(timer2); -+ } -+ } -+ } -+} -+ -+static inline void wm9712_lcd_sync_off(void) { -+ CCNT_OFF(); -+} -+#endif -+ - /* - * Read a sample from the WM9712 adc in polling mode. - */ -@@ -260,6 +300,9 @@ - /* set up digitiser */ - if (adcsel & 0x8000) - adcsel = ((adcsel & 0x7fff) + 3) << 12; -+ #ifdef CONFIG_MACH_TOSA -+ wm9712_lcd_sync_on(wm, adcsel); -+ #endif - wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, adcsel | WM97XX_POLL | WM97XX_DELAY(delay)); - - /* wait 3 AC97 time slots + delay for conversion */ -@@ -282,6 +325,10 @@ - - *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); - -+ #ifdef CONFIG_MACH_TOSA -+ wm9712_lcd_sync_off(); -+ #endif -+ - /* check we have correct sample */ - if ((*sample & WM97XX_ADCSEL_MASK) != adcsel) { - dbg ("adc wrong sample, read %x got %x", adcsel, -@@ -303,11 +350,12 @@ - static int wm9712_poll_touch(struct wm97xx* wm, struct wm97xx_data *data) - { - int rc; -- - if ((rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_X, &data->x)) != RC_VALID) - return rc; -+ - if ((rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_Y, &data->y)) != RC_VALID) - return rc; -+ - if (pil && !five_wire) { - if ((rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_PRES, &data->p)) != RC_VALID) - return rc; -Index: linux-tosa/drivers/input/touchscreen/wm97xx-core.c -=================================================================== ---- linux-tosa.orig/drivers/input/touchscreen/wm97xx-core.c 2006-08-29 16:52:36.008543280 +0100 -+++ linux-tosa/drivers/input/touchscreen/wm97xx-core.c 2006-08-29 16:52:50.924275744 +0100 -@@ -2,7 +2,7 @@ - * wm97xx-core.c -- Touch screen driver core for Wolfson WM9705, WM9712 - * and WM9713 AC97 Codecs. - * -- * Copyright 2003, 2004, 2005 Wolfson Microelectronics PLC. -+ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC. - * Author: Liam Girdwood - * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com - * Parts Copyright : Ian Molton <spyro@f2s.com> -@@ -67,6 +67,9 @@ - * GPIOs) and 2.6 power management. - * 29th Nov 2004 Added WM9713 support. - * 4th Jul 2005 Moved codec specific code out to seperate files. -+ * 29th Aug 2006 Mike Arthur <mike@mikearthur.co.uk> -+ * Added fixes for Sharp SL-6000 (Tosa) LCD noise causing -+ * touchscreen interference. - */ - - #include <linux/module.h> -@@ -94,6 +97,7 @@ - static DECLARE_MUTEX(gpio_sem); - static LIST_HEAD(wm97xx_misc_list); - static struct wm97xx* wm_codec = NULL; -+struct wm97xx_machinfo *wm_machinfo; - - /* - * WM97xx - enable/disable AUX ADC sysfs -@@ -832,6 +836,23 @@ - mdev->remove(wm_codec); - } - -+#ifdef CONFIG_MACH_TOSA -+/* On the Sharp SL-6000 (Tosa), due to a noisy LCD, we need to perform a wait -+ * before sampling the Y axis of the touchscreen */ -+unsigned long wm97xx_calc_lcd_waittime(struct wm97xx *wm) { -+ unsigned long hsync_time = wm_machinfo->get_hsync_time(); -+ return hsync_time; -+} -+ -+void wm97xx_set_machinfo(struct wm97xx_machinfo *machinfo) { -+ wm_machinfo = machinfo; -+} -+ -+void wm97xx_unset_machinfo() { -+ wm_machinfo = NULL; -+} -+#endif -+ - static struct device_driver wm97xx_driver = { - .name = "ac97", - .bus = &ac97_bus_type, -@@ -861,6 +882,9 @@ - EXPORT_SYMBOL_GPL(wm97xx_reg_write); - EXPORT_SYMBOL_GPL(wm97xx_register_misc_dev); - EXPORT_SYMBOL_GPL(wm97xx_unregister_misc_dev); -+EXPORT_SYMBOL_GPL(wm97xx_calc_lcd_waittime); -+EXPORT_SYMBOL_GPL(wm97xx_set_machinfo); -+EXPORT_SYMBOL_GPL(wm97xx_unset_machinfo); - - module_init(wm97xx_init); - module_exit(wm97xx_exit); -Index: linux-tosa/include/linux/wm97xx.h -=================================================================== ---- linux-tosa.orig/include/linux/wm97xx.h 2006-08-29 16:52:36.008543280 +0100 -+++ linux-tosa/include/linux/wm97xx.h 2006-08-29 16:52:50.924275744 +0100 -@@ -207,6 +207,7 @@ - - struct wm97xx; - extern struct wm97xx_codec_drv wm97xx_codec; -+extern struct wm97xx_machinfo *wm_machinfo; - - /* - * Codec driver interface - allows mapping to WM9705/12/13 and newer codecs -@@ -253,6 +254,11 @@ - struct list_head list; - }; - -+struct wm97xx_machinfo { -+ unsigned long (*get_hsync_time)(void); -+ void (*wait_hsync)(void); -+}; -+ - int wm97xx_register_misc_dev(struct wm97xx_misc_dev* mdev); - void wm97xx_unregister_misc_dev(struct wm97xx_misc_dev* mdev); - -@@ -281,4 +287,9 @@ - int wm97xx_acc_startup(struct wm97xx* wm); - void wm97xx_acc_shutdown(struct wm97xx* wm); - -+ -+unsigned long wm97xx_calc_lcd_waittime(struct wm97xx *wm); -+void wm97xx_set_machinfo(struct wm97xx_machinfo *machinfo); -+void wm97xx_unset_machinfo(void); -+ - #endif diff --git a/meta/packages/linux/linux-rp-2.6.23/mmcsd_no_scr_check-r2.patch b/meta/packages/linux/linux-rp-2.6.23/mmcsd_no_scr_check-r2.patch deleted file mode 100644 index ac2245f088..0000000000 --- a/meta/packages/linux/linux-rp-2.6.23/mmcsd_no_scr_check-r2.patch +++ /dev/null @@ -1,29 +0,0 @@ ---- - drivers/mmc/core/sd.c | 11 ++++++----- - 1 file changed, 6 insertions(+), 5 deletions(-) - -Index: linux-2.6.23/drivers/mmc/core/sd.c -=================================================================== ---- linux-2.6.23.orig/drivers/mmc/core/sd.c 2007-10-17 11:33:26.000000000 +0200 -+++ linux-2.6.23/drivers/mmc/core/sd.c 2007-10-17 11:33:49.000000000 +0200 -@@ -173,14 +173,15 @@ - - scr_struct = UNSTUFF_BITS(resp, 60, 4); - if (scr_struct != 0) { -- printk(KERN_ERR "%s: unrecognised SCR structure version %d\n", -+ printk(KERN_WARNING "%s: unrecognised SCR structure version %d\n", - mmc_hostname(card->host), scr_struct); -- return -EINVAL; -+ scr->sda_vsn = 0; -+ scr->bus_widths = 0; -+ } else { -+ scr->sda_vsn = UNSTUFF_BITS(resp, 56, 4); -+ scr->bus_widths = UNSTUFF_BITS(resp, 48, 4); - } - -- scr->sda_vsn = UNSTUFF_BITS(resp, 56, 4); -- scr->bus_widths = UNSTUFF_BITS(resp, 48, 4); -- - return 0; - } - diff --git a/meta/packages/linux/linux-rp-2.6.23/pda-power.patch b/meta/packages/linux/linux-rp-2.6.23/pda-power.patch deleted file mode 100644 index face2f4ef2..0000000000 --- a/meta/packages/linux/linux-rp-2.6.23/pda-power.patch +++ /dev/null @@ -1,3373 +0,0 @@ ---- - arch/arm/Kconfig | 2 - drivers/Kconfig | 2 - drivers/Makefile | 1 - drivers/power/Kconfig | 70 +++++ - drivers/power/Makefile | 28 ++ - drivers/power/adc_battery.c | 278 +++++++++++++++++++++ - drivers/power/apm_power.c | 247 +++++++++++++++++++ - drivers/power/ds2760_battery.c | 475 +++++++++++++++++++++++++++++++++++++ - drivers/power/micro_battery.c | 257 ++++++++++++++++++++ - drivers/power/olpc_battery.c | 302 +++++++++++++++++++++++ - drivers/power/pda_power.c | 263 ++++++++++++++++++++ - drivers/power/pmu_battery.c | 215 ++++++++++++++++ - drivers/power/power_supply.h | 42 +++ - drivers/power/power_supply_core.c | 168 +++++++++++++ - drivers/power/power_supply_leds.c | 188 ++++++++++++++ - drivers/power/power_supply_sysfs.c | 289 ++++++++++++++++++++++ - drivers/power/simpad-battery.c | 242 ++++++++++++++++++ - include/linux/power_supply.h | 175 +++++++++++++ - 18 files changed, 3244 insertions(+) - -Index: linux-2.6.22/drivers/power/adc_battery.c -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/drivers/power/adc_battery.c 2007-08-23 12:26:28.000000000 +0200 -@@ -0,0 +1,278 @@ -+/* -+ * Copyright (c) 2007 Paul Sokolovsky -+ * -+ * 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 -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ */ -+ -+//#define DEBUG -+ -+#include <linux/module.h> -+#include <linux/interrupt.h> -+#include <linux/pm.h> -+#include <linux/delay.h> -+#include <linux/workqueue.h> -+#include <linux/platform_device.h> -+#include <linux/power_supply.h> -+#include <linux/adc.h> -+#include <linux/adc_battery.h> -+ -+#include <asm/irq.h> -+ -+#define PIN_NO_VOLT 0 -+#define PIN_NO_CURR 1 -+#define PIN_NO_TEMP 2 -+ -+struct battery_adc_priv { -+ struct power_supply batt_cdev; -+ -+ struct battery_adc_platform_data *pdata; -+ -+ struct adc_request req; -+ struct adc_sense pins[3]; -+ struct adc_sense last_good_pins[3]; -+ -+ struct workqueue_struct *wq; -+ struct delayed_work work; -+}; -+ -+/* -+ * Battery properties -+ */ -+ -+static int adc_battery_get_property(struct power_supply *psy, -+ enum power_supply_property psp, -+ union power_supply_propval *val) -+{ -+ struct battery_adc_priv* drvdata = (struct battery_adc_priv*)psy; -+ int voltage; -+ -+ switch (psp) { -+ case POWER_SUPPLY_PROP_STATUS: -+ val->intval = drvdata->pdata->charge_status; -+ break; -+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: -+ val->intval = drvdata->pdata->battery_info.voltage_max_design; -+ break; -+ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: -+ val->intval = drvdata->pdata->battery_info.voltage_min_design; -+ break; -+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: -+ val->intval = drvdata->pdata->battery_info.charge_full_design; -+ break; -+ case POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN: -+ val->intval = drvdata->pdata->battery_info.charge_empty_design; -+ break; -+ case POWER_SUPPLY_PROP_VOLTAGE_NOW: -+ val->intval = drvdata->last_good_pins[PIN_NO_VOLT].value * drvdata->pdata->voltage_mult; -+ break; -+ case POWER_SUPPLY_PROP_CURRENT_NOW: -+ val->intval = drvdata->last_good_pins[PIN_NO_CURR].value * drvdata->pdata->current_mult; -+ break; -+ case POWER_SUPPLY_PROP_CHARGE_NOW: -+ /* We do calculations in mX, not uX, because todo it in uX we should use "long long"s, -+ * which is a mess (need to use do_div) when you need divide operation). */ -+ voltage = drvdata->last_good_pins[PIN_NO_VOLT].value * drvdata->pdata->voltage_mult; -+ val->intval = ((voltage/1000 - drvdata->pdata->battery_info.voltage_min_design/1000) * -+ (drvdata->pdata->battery_info.charge_full_design/1000 - -+ drvdata->pdata->battery_info.charge_empty_design/1000)) / -+ (drvdata->pdata->battery_info.voltage_max_design/1000 - -+ drvdata->pdata->battery_info.voltage_min_design/1000); -+ val->intval *= 1000; /* convert final result to uX */ -+ break; -+ case POWER_SUPPLY_PROP_TEMP: -+ val->intval = drvdata->last_good_pins[PIN_NO_TEMP].value * drvdata->pdata->temperature_mult / 1000; -+ break; -+ default: -+ return -EINVAL; -+ }; -+ return 0; -+} -+ -+/* -+ * Driver body -+ */ -+ -+static void adc_battery_query(struct battery_adc_priv *drvdata) -+{ -+ struct battery_adc_platform_data *pdata = drvdata->pdata; -+ int powered, charging; -+ -+ adc_request_sample(&drvdata->req); -+ -+ powered = power_supply_am_i_supplied(&drvdata->batt_cdev); -+ charging = pdata->is_charging ? pdata->is_charging() : -1; -+ -+ if (powered && charging) -+ pdata->charge_status = POWER_SUPPLY_STATUS_CHARGING; -+ else if (powered && !charging && charging != -1) -+ pdata->charge_status = POWER_SUPPLY_STATUS_FULL; -+ else -+ pdata->charge_status = POWER_SUPPLY_STATUS_DISCHARGING; -+ -+ /* Throw away invalid samples, this may happen soon after resume for example. */ -+ if (drvdata->pins[PIN_NO_VOLT].value > 0) { -+ memcpy(drvdata->last_good_pins, drvdata->pins, sizeof(drvdata->pins)); -+#ifdef DEBUG -+ printk("%d %d %d\n", drvdata->pins[PIN_NO_VOLT].value, -+ drvdata->pins[PIN_NO_CURR].value, -+ drvdata->pins[PIN_NO_TEMP].value); -+#endif -+ } -+} -+ -+static void adc_battery_charge_power_changed(struct power_supply *bat) -+{ -+ struct battery_adc_priv *drvdata = (struct battery_adc_priv*)bat; -+ cancel_delayed_work(&drvdata->work); -+ queue_delayed_work(drvdata->wq, &drvdata->work, 0); -+} -+ -+static void adc_battery_work_func(struct work_struct *work) -+{ -+ struct delayed_work *delayed_work = container_of(work, struct delayed_work, work); -+ struct battery_adc_priv *drvdata = container_of(delayed_work, struct battery_adc_priv, work); -+ -+ adc_battery_query(drvdata); -+ power_supply_changed(&drvdata->batt_cdev); -+ -+ queue_delayed_work(drvdata->wq, &drvdata->work, (5000 * HZ) / 1000); -+} -+ -+static int adc_battery_probe(struct platform_device *pdev) -+{ -+ int retval; -+ struct battery_adc_platform_data *pdata = pdev->dev.platform_data; -+ struct battery_adc_priv *drvdata; -+ int i, j; -+ enum power_supply_property props[] = { -+ POWER_SUPPLY_PROP_STATUS, -+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, -+ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, -+ POWER_SUPPLY_PROP_VOLTAGE_NOW, -+ POWER_SUPPLY_PROP_CURRENT_NOW, -+ POWER_SUPPLY_PROP_CHARGE_NOW, -+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, -+ POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN, -+ POWER_SUPPLY_PROP_TEMP, -+ }; -+ -+ // Initialize ts data structure. -+ drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL); -+ if (!drvdata) -+ return -ENOMEM; -+ -+ drvdata->batt_cdev.name = pdata->battery_info.name; -+ drvdata->batt_cdev.use_for_apm = pdata->battery_info.use_for_apm; -+ drvdata->batt_cdev.num_properties = ARRAY_SIZE(props); -+ drvdata->batt_cdev.get_property = adc_battery_get_property; -+ drvdata->batt_cdev.external_power_changed = -+ adc_battery_charge_power_changed; -+ -+ if (!pdata->voltage_pin) { -+ drvdata->batt_cdev.num_properties--; -+ props[3] = -1; -+ } -+ if (!pdata->current_pin) { -+ drvdata->batt_cdev.num_properties--; -+ props[4] = -1; -+ } -+ if (!pdata->temperature_pin) { -+ drvdata->batt_cdev.num_properties--; -+ props[8] = -1; -+ } -+ -+ drvdata->batt_cdev.properties = kmalloc( -+ sizeof(*drvdata->batt_cdev.properties) * -+ drvdata->batt_cdev.num_properties, GFP_KERNEL); -+ if (!drvdata->batt_cdev.properties) -+ return -ENOMEM; -+ -+ j = 0; -+ for (i = 0; i < ARRAY_SIZE(props); i++) { -+ if (props[i] == -1) -+ continue; -+ drvdata->batt_cdev.properties[j++] = props[i]; -+ } -+ -+ retval = power_supply_register(&pdev->dev, &drvdata->batt_cdev); -+ if (retval) { -+ printk("adc-battery: Error registering battery classdev"); -+ return retval; -+ } -+ -+ drvdata->req.senses = drvdata->pins; -+ drvdata->req.num_senses = ARRAY_SIZE(drvdata->pins); -+ drvdata->pins[PIN_NO_VOLT].name = pdata->voltage_pin; -+ drvdata->pins[PIN_NO_CURR].name = pdata->current_pin; -+ drvdata->pins[PIN_NO_TEMP].name = pdata->temperature_pin; -+ -+ adc_request_register(&drvdata->req); -+ -+ /* Here we assume raw values in mV */ -+ if (!pdata->voltage_mult) -+ pdata->voltage_mult = 1000; -+ /* Here we assume raw values in mA */ -+ if (!pdata->current_mult) -+ pdata->current_mult = 1000; -+ /* Here we assume raw values in 1/10 C */ -+ if (!pdata->temperature_mult) -+ pdata->temperature_mult = 1000; -+ -+ drvdata->pdata = pdata; -+ pdata->drvdata = drvdata; /* Seems ugly, we need better solution */ -+ -+ platform_set_drvdata(pdev, drvdata); -+ -+ // Load initial values ASAP -+ adc_battery_query(drvdata); -+ -+ // Still schedule next sampling soon -+ INIT_DELAYED_WORK(&drvdata->work, adc_battery_work_func); -+ drvdata->wq = create_workqueue(pdev->dev.bus_id); -+ if (!drvdata->wq) -+ return -ESRCH; -+ -+ queue_delayed_work(drvdata->wq, &drvdata->work, (5000 * HZ) / 1000); -+ -+ return retval; -+} -+ -+static int adc_battery_remove(struct platform_device *pdev) -+{ -+ struct battery_adc_priv *drvdata = platform_get_drvdata(pdev); -+ cancel_delayed_work(&drvdata->work); -+ destroy_workqueue(drvdata->wq); -+ power_supply_unregister(&drvdata->batt_cdev); -+ adc_request_unregister(&drvdata->req); -+ kfree(drvdata->batt_cdev.properties); -+ return 0; -+} -+ -+static struct platform_driver adc_battery_driver = { -+ .driver = { -+ .name = "adc-battery", -+ }, -+ .probe = adc_battery_probe, -+ .remove = adc_battery_remove, -+}; -+ -+static int __init adc_battery_init(void) -+{ -+ return platform_driver_register(&adc_battery_driver); -+} -+ -+static void __exit adc_battery_exit(void) -+{ -+ platform_driver_unregister(&adc_battery_driver); -+} -+ -+module_init(adc_battery_init) -+module_exit(adc_battery_exit) -+ -+MODULE_AUTHOR("Paul Sokolovsky"); -+MODULE_DESCRIPTION("Battery driver for ADC device"); -+MODULE_LICENSE("GPL"); -Index: linux-2.6.22/drivers/power/apm_power.c -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/drivers/power/apm_power.c 2007-08-23 12:13:52.000000000 +0200 -@@ -0,0 +1,247 @@ -+/* -+ * Copyright (c) 2007 Anton Vorontsov <cbou@mail.ru> -+ * Copyright (c) 2007 Eugeny Boger <eugenyboger@dgap.mipt.ru> -+ * -+ * Author: Eugeny Boger <eugenyboger@dgap.mipt.ru> -+ * -+ * Use consistent with the GNU GPL is permitted, -+ * provided that this copyright notice is -+ * preserved in its entirety in all copies and derived works. -+ */ -+ -+#include <linux/module.h> -+#include <linux/power_supply.h> -+#include <linux/apm-emulation.h> -+ -+#define PSY_PROP(psy, prop, val) psy->get_property(psy, \ -+ POWER_SUPPLY_PROP_##prop, val) -+ -+#define _MPSY_PROP(prop, val) main_battery->get_property(main_battery, \ -+ prop, val) -+ -+#define MPSY_PROP(prop, val) _MPSY_PROP(POWER_SUPPLY_PROP_##prop, val) -+ -+static struct power_supply *main_battery; -+ -+static void find_main_battery(void) -+{ -+ struct device *dev; -+ struct power_supply *bat, *batm; -+ union power_supply_propval full; -+ int max_charge = 0; -+ -+ main_battery = NULL; -+ batm = NULL; -+ list_for_each_entry(dev, &power_supply_class->devices, node) { -+ bat = dev_get_drvdata(dev); -+ /* If none of battery devices cantains 'use_for_apm' flag, -+ choice one with maximum design charge */ -+ if (!PSY_PROP(bat, CHARGE_FULL_DESIGN, &full)) { -+ if (full.intval > max_charge) { -+ batm = bat; -+ max_charge = full.intval; -+ } -+ } -+ -+ if (bat->use_for_apm) -+ main_battery = bat; -+ } -+ if (!main_battery) -+ main_battery = batm; -+ -+ return; -+} -+ -+static int calculate_time(int status) -+{ -+ union power_supply_propval charge_full, charge_empty; -+ union power_supply_propval charge, I; -+ -+ if (MPSY_PROP(CHARGE_FULL, &charge_full)) { -+ /* if battery can't report this property, use design value */ -+ if (MPSY_PROP(CHARGE_FULL_DESIGN, &charge_full)) -+ return -1; -+ } -+ -+ if (MPSY_PROP(CHARGE_EMPTY, &charge_empty)) { -+ /* if battery can't report this property, use design value */ -+ if (MPSY_PROP(CHARGE_EMPTY_DESIGN, &charge_empty)) -+ charge_empty.intval = 0; -+ } -+ -+ if (MPSY_PROP(CHARGE_AVG, &charge)) { -+ /* if battery can't report average value, use momentary */ -+ if (MPSY_PROP(CHARGE_NOW, &charge)) -+ return -1; -+ } -+ -+ if (MPSY_PROP(CURRENT_AVG, &I)) { -+ /* if battery can't report average value, use momentary */ -+ if (MPSY_PROP(CURRENT_NOW, &I)) -+ return -1; -+ } -+ -+ if (I.intval == 0) -+ return 0; -+ else if (status == POWER_SUPPLY_STATUS_CHARGING) -+ return ((charge.intval - charge_full.intval) * 60L) / -+ I.intval; -+ else -+ return -((charge.intval - charge_empty.intval) * 60L) / -+ I.intval; -+} -+ -+static int calculate_capacity(int using_charge) -+{ -+ enum power_supply_property full_prop, empty_prop; -+ enum power_supply_property full_design_prop, empty_design_prop; -+ enum power_supply_property now_prop, avg_prop; -+ union power_supply_propval empty, full, cur; -+ int ret; -+ -+ if (using_charge) { -+ full_prop = POWER_SUPPLY_PROP_CHARGE_FULL; -+ empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY; -+ full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN; -+ empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN; -+ now_prop = POWER_SUPPLY_PROP_CHARGE_NOW; -+ avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG; -+ } -+ else { -+ full_prop = POWER_SUPPLY_PROP_ENERGY_FULL; -+ empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY; -+ full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN; -+ empty_design_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN; -+ now_prop = POWER_SUPPLY_PROP_ENERGY_NOW; -+ avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG; -+ } -+ -+ if (_MPSY_PROP(full_prop, &full)) { -+ /* if battery can't report this property, use design value */ -+ if (_MPSY_PROP(full_design_prop, &full)) -+ return -1; -+ } -+ -+ if (_MPSY_PROP(avg_prop, &cur)) { -+ /* if battery can't report average value, use momentary */ -+ if (_MPSY_PROP(now_prop, &cur)) -+ return -1; -+ } -+ -+ if (_MPSY_PROP(empty_prop, &empty)) { -+ /* if battery can't report this property, use design value */ -+ if (_MPSY_PROP(empty_design_prop, &empty)) -+ empty.intval = 0; -+ } -+ -+ if (full.intval - empty.intval) -+ ret = ((cur.intval - empty.intval) * 100L) / -+ (full.intval - empty.intval); -+ else -+ return -1; -+ -+ if (ret > 100) -+ return 100; -+ else if (ret < 0) -+ return 0; -+ -+ return ret; -+} -+ -+static void apm_battery_apm_get_power_status(struct apm_power_info *info) -+{ -+ union power_supply_propval status; -+ union power_supply_propval capacity, time_to_full, time_to_empty; -+ -+ down(&power_supply_class->sem); -+ find_main_battery(); -+ if (!main_battery) { -+ up(&power_supply_class->sem); -+ return; -+ } -+ -+ /* status */ -+ -+ if (MPSY_PROP(STATUS, &status)) -+ status.intval = POWER_SUPPLY_STATUS_UNKNOWN; -+ -+ /* ac line status */ -+ -+ if ((status.intval == POWER_SUPPLY_STATUS_CHARGING) || -+ (status.intval == POWER_SUPPLY_STATUS_NOT_CHARGING) || -+ (status.intval == POWER_SUPPLY_STATUS_FULL)) -+ info->ac_line_status = APM_AC_ONLINE; -+ else -+ info->ac_line_status = APM_AC_OFFLINE; -+ -+ /* battery life (i.e. capacity, in percents) */ -+ -+ if (MPSY_PROP(CAPACITY, &capacity) == 0) -+ info->battery_life = capacity.intval; -+ else { -+ /* try calculate using energy */ -+ info->battery_life = calculate_capacity(0); -+ /* if failed try calculate using charge instead */ -+ if (info->battery_life == -1) -+ info->battery_life = calculate_capacity(1); -+ } -+ -+ /* charging status */ -+ -+ if (status.intval == POWER_SUPPLY_STATUS_CHARGING) -+ info->battery_status = APM_BATTERY_STATUS_CHARGING; -+ else { -+ if (info->battery_life > 50) -+ info->battery_status = APM_BATTERY_STATUS_HIGH; -+ else if (info->battery_life > 5) -+ info->battery_status = APM_BATTERY_STATUS_LOW; -+ else -+ info->battery_status = APM_BATTERY_STATUS_CRITICAL; -+ } -+ info->battery_flag = info->battery_status; -+ -+ /* time */ -+ -+ info->units = APM_UNITS_MINS; -+ -+ if (status.intval == POWER_SUPPLY_STATUS_CHARGING) { -+ if (MPSY_PROP(TIME_TO_FULL_AVG, &time_to_full)) { -+ if (MPSY_PROP(TIME_TO_FULL_NOW, &time_to_full)) -+ info->time = calculate_time(status.intval); -+ else -+ info->time = time_to_full.intval / 60; -+ } -+ } -+ else { -+ if (MPSY_PROP(TIME_TO_EMPTY_AVG, &time_to_empty)) { -+ if (MPSY_PROP(TIME_TO_EMPTY_NOW, &time_to_empty)) -+ info->time = calculate_time(status.intval); -+ else -+ info->time = time_to_empty.intval / 60; -+ } -+ } -+ -+ up(&power_supply_class->sem); -+ return; -+} -+ -+static int __init apm_battery_init(void) -+{ -+ printk(KERN_INFO "APM Battery Driver\n"); -+ -+ apm_get_power_status = apm_battery_apm_get_power_status; -+ return 0; -+} -+ -+static void __exit apm_battery_exit(void) -+{ -+ apm_get_power_status = NULL; -+ return; -+} -+ -+module_init(apm_battery_init); -+module_exit(apm_battery_exit); -+ -+MODULE_AUTHOR("Eugeny Boger <eugenyboger@dgap.mipt.ru>"); -+MODULE_DESCRIPTION("APM emulation driver for battery monitoring class"); -+MODULE_LICENSE("GPL"); -Index: linux-2.6.22/drivers/power/ds2760_battery.c -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/drivers/power/ds2760_battery.c 2007-08-23 12:13:52.000000000 +0200 -@@ -0,0 +1,475 @@ -+/* -+ * Driver for batteries with DS2760 chips inside. -+ * -+ * Copyright (c) 2007 Anton Vorontsov -+ * 2004-2007 Matt Reimer -+ * 2004 Szabolcs Gyurko -+ * -+ * Use consistent with the GNU GPL is permitted, -+ * provided that this copyright notice is -+ * preserved in its entirety in all copies and derived works. -+ * -+ * Author: Anton Vorontsov <cbou@mail.ru> -+ * February 2007 -+ * -+ * Matt Reimer <mreimer@vpop.net> -+ * April 2004, 2005, 2007 -+ * -+ * Szabolcs Gyurko <szabolcs.gyurko@tlt.hu> -+ * September 2004 -+ */ -+ -+#include <linux/module.h> -+#include <linux/param.h> -+#include <linux/jiffies.h> -+#include <linux/workqueue.h> -+#include <linux/pm.h> -+#include <linux/platform_device.h> -+#include <linux/power_supply.h> -+ -+#include "../w1/w1.h" -+#include "../w1/slaves/w1_ds2760.h" -+ -+struct ds2760_device_info { -+ struct device *dev; -+ -+ /* DS2760 data, valid after calling ds2760_battery_read_status() */ -+ unsigned long update_time; /* jiffies when data read */ -+ char raw[DS2760_DATA_SIZE]; /* raw DS2760 data */ -+ int voltage_raw; /* units of 4.88 mV */ -+ int voltage_uV; /* units of uV */ -+ int current_raw; /* units of 0.625 mA */ -+ int current_uA; /* units of uA */ -+ int accum_current_raw; /* units of 0.25 mAh */ -+ int accum_current_uAh; /* units of uAh */ -+ int temp_raw; /* units of 0.125 C */ -+ int temp_C; /* units of 0.1 C */ -+ int rated_capacity; /* units of uAh */ -+ int rem_capacity; /* percentage */ -+ int full_active_uAh; /* units of uAh */ -+ int empty_uAh; /* units of uAh */ -+ int life_sec; /* units of seconds */ -+ int charge_status; /* POWER_SUPPLY_STATUS_* */ -+ -+ int full_counter; -+ struct power_supply bat; -+ struct device *w1_dev; -+ struct workqueue_struct *monitor_wqueue; -+ struct delayed_work monitor_work; -+}; -+ -+static unsigned int cache_time = 1000; -+module_param(cache_time, uint, 0644); -+MODULE_PARM_DESC(cache_time, "cache time in milliseconds"); -+ -+/* Some batteries have their rated capacity stored a N * 10 mAh, while -+ * others use an index into this table. */ -+static int rated_capacities[] = { -+ 0, -+ 920, /* Samsung */ -+ 920, /* BYD */ -+ 920, /* Lishen */ -+ 920, /* NEC */ -+ 1440, /* Samsung */ -+ 1440, /* BYD */ -+ 1440, /* Lishen */ -+ 1440, /* NEC */ -+ 2880, /* Samsung */ -+ 2880, /* BYD */ -+ 2880, /* Lishen */ -+ 2880 /* NEC */ -+}; -+ -+/* array is level at temps 0C, 10C, 20C, 30C, 40C -+ * temp is in Celsius */ -+static int battery_interpolate(int array[], int temp) -+{ -+ int index, dt; -+ -+ if (temp <= 0) -+ return array[0]; -+ if (temp >= 40) -+ return array[4]; -+ -+ index = temp / 10; -+ dt = temp % 10; -+ -+ return array[index] + (((array[index + 1] - array[index]) * dt) / 10); -+} -+ -+static int ds2760_battery_read_status(struct ds2760_device_info *di) -+{ -+ int ret, i, start, count, scale[5]; -+ -+ if (di->update_time && time_before(jiffies, di->update_time + -+ msecs_to_jiffies(cache_time))) -+ return 0; -+ -+ /* The first time we read the entire contents of SRAM/EEPROM, -+ * but after that we just read the interesting bits that change. */ -+ if (di->update_time == 0) { -+ start = 0; -+ count = DS2760_DATA_SIZE; -+ } -+ else { -+ start = DS2760_VOLTAGE_MSB; -+ count = DS2760_TEMP_LSB - start + 1; -+ } -+ -+ ret = w1_ds2760_read(di->w1_dev, di->raw + start, start, count); -+ if (ret != count) { -+ dev_warn(di->dev, "call to w1_ds2760_read failed (0x%p)\n", -+ di->w1_dev); -+ return 1; -+ } -+ -+ di->update_time = jiffies; -+ -+ /* DS2760 reports voltage in units of 4.88mV, but the battery class -+ * reports in units of uV, so convert by multiplying by 4880. */ -+ di->voltage_raw = (di->raw[DS2760_VOLTAGE_MSB] << 3) | -+ (di->raw[DS2760_VOLTAGE_LSB] >> 5); -+ di->voltage_uV = di->voltage_raw * 4880; -+ -+ /* DS2760 reports current in signed units of 0.625mA, but the battery -+ * class reports in units of uA, so convert by multiplying by 625. */ -+ di->current_raw = -+ (((signed char)di->raw[DS2760_CURRENT_MSB]) << 5) | -+ (di->raw[DS2760_CURRENT_LSB] >> 3); -+ di->current_uA = di->current_raw * 625; -+ -+ /* DS2760 reports accumulated current in signed units of 0.25mAh. */ -+ di->accum_current_raw = -+ (((signed char)di->raw[DS2760_CURRENT_ACCUM_MSB]) << 8) | -+ di->raw[DS2760_CURRENT_ACCUM_LSB]; -+ di->accum_current_uAh = di->accum_current_raw * 250; -+ -+ /* DS2760 reports temperature in signed units of 0.125C, but the -+ * battery class reports in units of 1/10 C, so we convert by -+ * multiplying by .125 * 10 = 1.25. */ -+ di->temp_raw = (((signed char)di->raw[DS2760_TEMP_MSB]) << 3) | -+ (di->raw[DS2760_TEMP_LSB] >> 5); -+ di->temp_C = di->temp_raw + (di->temp_raw / 4); -+ -+ /* At least some battery monitors (e.g. HP iPAQ) store the battery's -+ * maximum rated capacity. */ -+ if (di->raw[DS2760_RATED_CAPACITY] < ARRAY_SIZE(rated_capacities)) -+ di->rated_capacity = rated_capacities[ -+ (unsigned int)di->raw[DS2760_RATED_CAPACITY]]; -+ else -+ di->rated_capacity = di->raw[DS2760_RATED_CAPACITY] * 10; -+ -+ di->rated_capacity *= 1000; /* convert to uAh */ -+ -+ /* Calculate the full level at the present temperature. */ -+ di->full_active_uAh = di->raw[DS2760_ACTIVE_FULL] << 8 | -+ di->raw[DS2760_ACTIVE_FULL + 1]; -+ -+ scale[0] = di->raw[DS2760_ACTIVE_FULL] << 8 | -+ di->raw[DS2760_ACTIVE_FULL + 1]; -+ for (i = 1; i < 5; i++) -+ scale[i] = scale[i - 1] + di->raw[DS2760_ACTIVE_FULL + 2 + i]; -+ -+ di->full_active_uAh = battery_interpolate(scale, di->temp_C / 10); -+ di->full_active_uAh *= 1000; /* convert to uAh */ -+ -+ /* Calculate the empty level at the present temperature. */ -+ scale[4] = di->raw[DS2760_ACTIVE_EMPTY + 4]; -+ for (i = 3; i >= 0; i--) -+ scale[i] = scale[i + 1] + di->raw[DS2760_ACTIVE_EMPTY + i]; -+ -+ di->empty_uAh = battery_interpolate(scale, di->temp_C / 10); -+ di->empty_uAh *= 1000; /* convert to uAh */ -+ -+ /* From Maxim Application Note 131: remaining capacity = -+ * ((ICA - Empty Value) / (Full Value - Empty Value)) x 100% */ -+ di->rem_capacity = ((di->accum_current_uAh - di->empty_uAh) * 100L) / -+ (di->full_active_uAh - di->empty_uAh); -+ -+ if (di->rem_capacity < 0) -+ di->rem_capacity = 0; -+ if (di->rem_capacity > 100) -+ di->rem_capacity = 100; -+ -+ if (di->current_uA) -+ di->life_sec = -((di->accum_current_uAh - di->empty_uAh) * -+ 3600L) / di->current_uA; -+ else -+ di->life_sec = 0; -+ -+ return 0; -+} -+ -+static void ds2760_battery_update_status(struct ds2760_device_info *di) -+{ -+ int old_charge_status = di->charge_status; -+ -+ ds2760_battery_read_status(di); -+ -+ if (di->charge_status == POWER_SUPPLY_STATUS_UNKNOWN) -+ di->full_counter = 0; -+ -+ if (power_supply_am_i_supplied(&di->bat)) { -+ if (di->current_uA > 10000) { -+ di->charge_status = POWER_SUPPLY_STATUS_CHARGING; -+ di->full_counter = 0; -+ } -+ else if (di->current_uA < -5000) { -+ if (di->charge_status != POWER_SUPPLY_STATUS_NOT_CHARGING) -+ dev_notice(di->dev, "not enough power to " -+ "charge\n"); -+ di->charge_status = POWER_SUPPLY_STATUS_NOT_CHARGING; -+ di->full_counter = 0; -+ } -+ else if (di->current_uA < 10000 && -+ di->charge_status != POWER_SUPPLY_STATUS_FULL) { -+ -+ /* Don't consider the battery to be full unless -+ * we've seen the current < 10 mA at least two -+ * consecutive times. */ -+ -+ di->full_counter++; -+ -+ if (di->full_counter < 2) -+ di->charge_status = POWER_SUPPLY_STATUS_CHARGING; -+ else { -+ unsigned char acr[2]; -+ int acr_val; -+ -+ /* acr is in units of 0.25 mAh */ -+ acr_val = di->full_active_uAh * 4L / 1000; -+ -+ acr[0] = acr_val >> 8; -+ acr[1] = acr_val & 0xff; -+ -+ if (w1_ds2760_write(di->w1_dev, acr, -+ DS2760_CURRENT_ACCUM_MSB, 2) < 2) -+ dev_warn(di->dev, -+ "ACR reset failed\n"); -+ -+ di->charge_status = POWER_SUPPLY_STATUS_FULL; -+ } -+ } -+ } -+ else { -+ di->charge_status = POWER_SUPPLY_STATUS_DISCHARGING; -+ di->full_counter = 0; -+ } -+ -+ if (di->charge_status != old_charge_status) -+ power_supply_changed(&di->bat); -+ -+ return; -+} -+ -+static void ds2760_battery_work(struct work_struct *work) -+{ -+ struct ds2760_device_info *di = container_of(work, -+ struct ds2760_device_info, monitor_work.work); -+ const int interval = HZ * 60; -+ -+ dev_dbg(di->dev, "%s\n", __FUNCTION__); -+ -+ ds2760_battery_update_status(di); -+ queue_delayed_work(di->monitor_wqueue, &di->monitor_work, interval); -+ -+ return; -+} -+ -+#define to_ds2760_device_info(x) container_of((x), struct ds2760_device_info, \ -+ bat); -+ -+static void ds2760_battery_external_power_changed(struct power_supply *psy) -+{ -+ struct ds2760_device_info *di = to_ds2760_device_info(psy); -+ -+ dev_dbg(di->dev, "%s\n", __FUNCTION__); -+ -+ cancel_delayed_work(&di->monitor_work); -+ queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ/10); -+ -+ return; -+} -+ -+static int ds2760_battery_get_property(struct power_supply *psy, -+ enum power_supply_property psp, -+ union power_supply_propval *val) -+{ -+ struct ds2760_device_info *di = to_ds2760_device_info(psy); -+ -+ switch (psp) { -+ case POWER_SUPPLY_PROP_STATUS: -+ val->intval = di->charge_status; -+ return 0; -+ default: -+ break; -+ } -+ -+ ds2760_battery_read_status(di); -+ -+ switch (psp) { -+ case POWER_SUPPLY_PROP_VOLTAGE_NOW: -+ val->intval = di->voltage_uV; -+ break; -+ case POWER_SUPPLY_PROP_CURRENT_NOW: -+ val->intval = di->current_uA; -+ break; -+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: -+ val->intval = di->rated_capacity; -+ break; -+ case POWER_SUPPLY_PROP_CHARGE_FULL: -+ val->intval = di->full_active_uAh; -+ break; -+ case POWER_SUPPLY_PROP_CHARGE_EMPTY: -+ val->intval = di->empty_uAh; -+ break; -+ case POWER_SUPPLY_PROP_CHARGE_NOW: -+ val->intval = di->accum_current_uAh; -+ break; -+ case POWER_SUPPLY_PROP_TEMP: -+ val->intval = di->temp_C; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static enum power_supply_property ds2760_battery_props[] = { -+ POWER_SUPPLY_PROP_STATUS, -+ POWER_SUPPLY_PROP_VOLTAGE_NOW, -+ POWER_SUPPLY_PROP_CURRENT_NOW, -+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, -+ POWER_SUPPLY_PROP_CHARGE_FULL, -+ POWER_SUPPLY_PROP_CHARGE_EMPTY, -+ POWER_SUPPLY_PROP_CHARGE_NOW, -+ POWER_SUPPLY_PROP_TEMP, -+}; -+ -+static int ds2760_battery_probe(struct platform_device *pdev) -+{ -+ int retval = 0; -+ struct ds2760_device_info *di; -+ struct ds2760_platform_data *pdata; -+ -+ di = kzalloc(sizeof(*di), GFP_KERNEL); -+ if (!di) { -+ retval = -ENOMEM; -+ goto di_alloc_failed; -+ } -+ -+ platform_set_drvdata(pdev, di); -+ -+ pdata = pdev->dev.platform_data; -+ di->dev = &pdev->dev; -+ di->w1_dev = pdev->dev.parent; -+ di->bat.name = pdev->dev.bus_id; -+ di->bat.type = POWER_SUPPLY_TYPE_BATTERY; -+ di->bat.properties = ds2760_battery_props; -+ di->bat.num_properties = ARRAY_SIZE(ds2760_battery_props); -+ di->bat.get_property = ds2760_battery_get_property; -+ di->bat.external_power_changed = -+ ds2760_battery_external_power_changed; -+ di->bat.use_for_apm = 1; -+ -+ di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN; -+ -+ retval = power_supply_register(&pdev->dev, &di->bat); -+ if (retval) { -+ dev_err(di->dev, "failed to register battery"); -+ goto batt_failed; -+ } -+ -+ INIT_DELAYED_WORK(&di->monitor_work, ds2760_battery_work); -+ di->monitor_wqueue = create_singlethread_workqueue(pdev->dev.bus_id); -+ if (!di->monitor_wqueue) { -+ retval = -ESRCH; -+ goto workqueue_failed; -+ } -+ queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ * 1); -+ -+ goto success; -+ -+workqueue_failed: -+ power_supply_unregister(&di->bat); -+batt_failed: -+ kfree(di); -+di_alloc_failed: -+success: -+ return retval; -+} -+ -+static int ds2760_battery_remove(struct platform_device *pdev) -+{ -+ struct ds2760_device_info *di = platform_get_drvdata(pdev); -+ -+ cancel_rearming_delayed_workqueue(di->monitor_wqueue, -+ &di->monitor_work); -+ destroy_workqueue(di->monitor_wqueue); -+ power_supply_unregister(&di->bat); -+ -+ return 0; -+} -+ -+#ifdef CONFIG_PM -+ -+static int ds2760_battery_suspend(struct platform_device *pdev, -+ pm_message_t state) -+{ -+ struct ds2760_device_info *di = platform_get_drvdata(pdev); -+ -+ di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN; -+ -+ return 0; -+} -+ -+static int ds2760_battery_resume(struct platform_device *pdev) -+{ -+ struct ds2760_device_info *di = platform_get_drvdata(pdev); -+ -+ di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN; -+ power_supply_changed(&di->bat); -+ -+ cancel_delayed_work(&di->monitor_work); -+ queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ); -+ -+ return 0; -+} -+ -+#else -+ -+#define ds2760_battery_suspend NULL -+#define ds2760_battery_resume NULL -+ -+#endif /* CONFIG_PM */ -+ -+static struct platform_driver ds2760_battery_driver = { -+ .driver = { -+ .name = "ds2760-battery", -+ }, -+ .probe = ds2760_battery_probe, -+ .remove = ds2760_battery_remove, -+ .suspend = ds2760_battery_suspend, -+ .resume = ds2760_battery_resume, -+}; -+ -+static int __init ds2760_battery_init(void) -+{ -+ return platform_driver_register(&ds2760_battery_driver); -+} -+ -+static void __exit ds2760_battery_exit(void) -+{ -+ platform_driver_unregister(&ds2760_battery_driver); -+ return; -+} -+ -+module_init(ds2760_battery_init); -+module_exit(ds2760_battery_exit); -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>, " -+ "Matt Reimer <mreimer@vpop.net>, " -+ "Anton Vorontsov <cbou@mail.ru>"); -+MODULE_DESCRIPTION("ds2760 battery driver"); -Index: linux-2.6.22/drivers/power/Kconfig -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/drivers/power/Kconfig 2007-08-23 12:13:52.000000000 +0200 -@@ -0,0 +1,70 @@ -+menuconfig POWER_SUPPLY -+ tristate "Power supply class support" -+ help -+ Say Y here to enable power supply class support. This allows -+ power supply (batteries, AC, USB) monitoring by userspace -+ via sysfs and uevent (if available) and/or APM kernel interface -+ (if selected below). -+ -+if POWER_SUPPLY -+ -+config POWER_SUPPLY_DEBUG -+ bool "Power supply debug" -+ help -+ Say Y here to enable debugging messages for power supply class -+ and drivers. -+ -+config PDA_POWER -+ tristate "Generic PDA/phone power driver" -+ help -+ Say Y here to enable generic power driver for PDAs and phones with -+ one or two external power supplies (AC/USB) connected to main and -+ backup batteries, and optional builtin charger. -+ -+config APM_POWER -+ tristate "APM emulation for class batteries" -+ depends on APM_EMULATION -+ help -+ Say Y here to enable support APM status emulation using -+ battery class devices. -+ -+config BATTERY_DS2760 -+ tristate "DS2760 battery driver (HP iPAQ & others)" -+ select W1 -+ select W1_SLAVE_DS2760 -+ help -+ Say Y here to enable support for batteries with ds2760 chip. -+ -+config BATTERY_PMU -+ tristate "Apple PMU battery" -+ depends on ADB_PMU -+ help -+ Say Y here to expose battery information on Apple machines -+ through the generic battery class. -+ -+config BATTERY_OLPC -+ tristate "One Laptop Per Child battery" -+ depends on X86_32 -+ help -+ Say Y to enable support for the battery on the OLPC laptop. -+ -+# drivers below are not in battery2-2.6 tree -+ -+config ADC_BATTERY -+ tristate "Generic ADC battery driver" -+ depends on ADC && POWER_SUPPLY -+ help -+ Say Y here to enable support for battery monitoring using generic ADC device. -+ -+config IPAQ_MICRO_BATTERY -+ tristate "HP iPAQ Micro ASIC battery driver" -+ depends on IPAQ_MICRO && POWER_SUPPLY -+ help -+ Choose this option if you want to monitor battery status on -+ Compaq/HP iPAQ h3100 h3600 -+ -+config MCP_UCB1x00_SIMPAD_BATTERY -+ tristate "SIMpad Battery Reading Support" -+ depends on MCP_UCB1x00 && POWER_SUPPLY -+ -+endif # POWER_SUPPLY -Index: linux-2.6.22/drivers/power/Makefile -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/drivers/power/Makefile 2007-08-23 12:13:52.000000000 +0200 -@@ -0,0 +1,28 @@ -+power_supply-objs := power_supply_core.o -+ -+ifeq ($(CONFIG_SYSFS),y) -+power_supply-objs += power_supply_sysfs.o -+endif -+ -+ifeq ($(CONFIG_LEDS_TRIGGERS),y) -+power_supply-objs += power_supply_leds.o -+endif -+ -+ifeq ($(CONFIG_POWER_SUPPLY_DEBUG),y) -+EXTRA_CFLAGS += -DDEBUG -+endif -+ -+obj-$(CONFIG_POWER_SUPPLY) += power_supply.o -+ -+obj-$(CONFIG_PDA_POWER) += pda_power.o -+obj-$(CONFIG_APM_POWER) += apm_power.o -+ -+obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o -+obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o -+obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o -+ -+# drivers below are not in battery2-2.6 tree -+ -+obj-$(CONFIG_ADC_BATTERY) += adc_battery.o -+obj-$(CONFIG_IPAQ_MICRO_BATTERY) += micro_battery.o -+obj-$(CONFIG_MCP_UCB1x00_SIMPAD_BATTERY) += simpad-battery.o -Index: linux-2.6.22/drivers/power/micro_battery.c -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/drivers/power/micro_battery.c 2007-08-23 12:25:20.000000000 +0200 -@@ -0,0 +1,257 @@ -+/* -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ * -+ * h3600 atmel micro companion support, battery subdevice -+ * based on previous kernel 2.4 version -+ * Author : Alessandro Gardich <gremlin@gremlin.it> -+ * -+ */ -+ -+ -+#include <linux/module.h> -+#include <linux/version.h> -+ -+#include <linux/init.h> -+#include <linux/fs.h> -+#include <linux/interrupt.h> -+#include <linux/sched.h> -+#include <linux/pm.h> -+#include <linux/sysctl.h> -+#include <linux/proc_fs.h> -+#include <linux/delay.h> -+#include <linux/device.h> -+#include <linux/power_supply.h> -+#include <linux/platform_device.h> -+#include <linux/timer.h> -+ -+#include <asm/arch/hardware.h> -+ -+#include <asm/arch/h3600.h> -+#include <asm/arch/SA-1100.h> -+ -+#include <asm/hardware/micro.h> -+ -+#define BATT_PERIOD 10*HZ -+ -+#define H3600_BATT_STATUS_HIGH 0x01 -+#define H3600_BATT_STATUS_LOW 0x02 -+#define H3600_BATT_STATUS_CRITICAL 0x04 -+#define H3600_BATT_STATUS_CHARGING 0x08 -+#define H3600_BATT_STATUS_CHARGEMAIN 0x10 -+#define H3600_BATT_STATUS_DEAD 0x20 /* Battery will not charge */ -+#define H3600_BATT_STATUS_NOTINSTALLED 0x20 /* For expansion pack batteries */ -+#define H3600_BATT_STATUS_FULL 0x40 /* Battery fully charged (and connected to AC) */ -+#define H3600_BATT_STATUS_NOBATTERY 0x80 -+#define H3600_BATT_STATUS_UNKNOWN 0xff -+ -+ -+//static struct power_supply_dev *micro_battery; -+ -+static micro_private_t *p_micro; -+ -+struct timer_list batt_timer; -+ -+struct { -+ int ac; -+ int update_time; -+ int chemistry; -+ int voltage; -+ int temperature; -+ int flag; -+} micro_battery; -+ -+static void micro_battery_receive (int len, unsigned char *data) { -+ if (0) { -+ printk(KERN_ERR "h3600_battery - AC = %02x\n", data[0]); -+ printk(KERN_ERR "h3600_battery - BAT1 chemistry = %02x\n", data[1]); -+ printk(KERN_ERR "h3600_battery - BAT1 voltage = %d %02x%02x\n", (data[3]<<8)+data[2], data[2], data[3]); -+ printk(KERN_ERR "h3600_battery - BAT1 status = %02x\n", data[4]); -+ } -+ -+ micro_battery.ac = data[0]; -+ micro_battery.chemistry = data[1]; -+ micro_battery.voltage = ((((unsigned short)data[3]<<8)+data[2]) * 5000L ) * 1000 / 1024; -+ micro_battery.flag = data[4]; -+ -+ if (len == 9) { -+ if (0) { -+ printk(KERN_ERR "h3600_battery - BAT2 chemistry = %02x\n", data[5]); -+ printk(KERN_ERR "h3600_battery - BAT2 voltage = %d %02x%02x\n", (data[7]<<8)+data[6], data[6], data[7]); -+ printk(KERN_ERR "h3600_battery - BAT2 status = %02x\n", data[8]); -+ } -+ } -+} -+ -+static void micro_temperature_receive (int len, unsigned char *data) { -+ micro_battery.temperature = ((unsigned short)data[1]<<8)+data[0]; -+} -+ -+void h3600_battery_read_status(unsigned long data) { -+ -+ if (++data % 2) -+ h3600_micro_tx_msg(0x09,0,NULL); -+ else -+ h3600_micro_tx_msg(0x06,0,NULL); -+ -+ batt_timer.expires += BATT_PERIOD; -+ batt_timer.data = data; -+ -+ add_timer(&batt_timer); -+} -+ -+int get_capacity(struct power_supply *b) { -+ switch (micro_battery.flag) { -+ case H3600_BATT_STATUS_HIGH : return 100; break; -+ case H3600_BATT_STATUS_LOW : return 50; break; -+ case H3600_BATT_STATUS_CRITICAL : return 5; break; -+ default: break; -+ } -+ return 0; -+} -+ -+int get_status(struct power_supply *b) { -+ -+ if (micro_battery.flag == H3600_BATT_STATUS_UNKNOWN) -+ return POWER_SUPPLY_STATUS_UNKNOWN; -+ -+ if (micro_battery.flag & H3600_BATT_STATUS_FULL) -+ return POWER_SUPPLY_STATUS_FULL; -+ -+ if ((micro_battery.flag & H3600_BATT_STATUS_CHARGING) || -+ (micro_battery.flag & H3600_BATT_STATUS_CHARGEMAIN)) -+ return POWER_SUPPLY_STATUS_CHARGING; -+ -+ return POWER_SUPPLY_STATUS_DISCHARGING; -+} -+ -+static int micro_batt_get_property(struct power_supply *b, -+ enum power_supply_property psp, -+ union power_supply_propval *val) -+{ -+ switch (psp) { -+ case POWER_SUPPLY_PROP_STATUS: -+ val->intval = get_status(b); -+ break; -+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: -+ val->intval = 4700000; -+ break; -+ case POWER_SUPPLY_PROP_CAPACITY: -+ val->intval = get_capacity(b); -+ break; -+ case POWER_SUPPLY_PROP_TEMP: -+ val->intval = micro_battery.temperature; -+ break; -+ case POWER_SUPPLY_PROP_VOLTAGE_NOW: -+ val->intval = micro_battery.voltage; -+ break; -+ default: -+ return -EINVAL; -+ }; -+ -+ return 0; -+} -+ -+static enum power_supply_property micro_batt_props[] = { -+ POWER_SUPPLY_PROP_STATUS, -+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, -+ POWER_SUPPLY_PROP_CAPACITY, -+ POWER_SUPPLY_PROP_TEMP, -+ POWER_SUPPLY_PROP_VOLTAGE_NOW, -+}; -+ -+static struct power_supply h3600_battery = { -+ .name = "main-battery", -+ .properties = micro_batt_props, -+ .num_properties = ARRAY_SIZE(micro_batt_props), -+ .get_property = micro_batt_get_property, -+ .use_for_apm = 1, -+}; -+ -+static int micro_batt_probe (struct platform_device *pdev) -+{ -+ if (1) printk(KERN_ERR "micro battery probe : begin\n"); -+ -+ power_supply_register(&pdev->dev, &h3600_battery); -+ -+ { /*--- callback ---*/ -+ p_micro = platform_get_drvdata(pdev); -+ spin_lock(p_micro->lock); -+ p_micro->h_batt = micro_battery_receive; -+ p_micro->h_temp = micro_temperature_receive; -+ spin_unlock(p_micro->lock); -+ } -+ -+ { /*--- timer ---*/ -+ init_timer(&batt_timer); -+ batt_timer.expires = jiffies + BATT_PERIOD; -+ batt_timer.data = 0; -+ batt_timer.function = h3600_battery_read_status; -+ -+ add_timer(&batt_timer); -+ } -+ -+ if (1) printk(KERN_ERR "micro battery probe : end\n"); -+ return 0; -+} -+ -+static int micro_batt_remove (struct platform_device *pdev) -+{ -+ power_supply_unregister(&h3600_battery); -+ { /*--- callback ---*/ -+ init_timer(&batt_timer); -+ p_micro->h_batt = NULL; -+ p_micro->h_temp = NULL; -+ spin_unlock(p_micro->lock); -+ } -+ { /*--- timer ---*/ -+ del_timer_sync(&batt_timer); -+ } -+ return 0; -+} -+ -+static int micro_batt_suspend ( struct platform_device *pdev, pm_message_t state) -+{ -+ { /*--- timer ---*/ -+ del_timer(&batt_timer); -+ } -+ return 0; -+} -+ -+static int micro_batt_resume ( struct platform_device *pdev) -+{ -+ { /*--- timer ---*/ -+ add_timer(&batt_timer); -+ } -+ return 0; -+} -+ -+struct platform_driver micro_batt_device_driver = { -+ .driver = { -+ .name = "h3600-micro-battery", -+ }, -+ .probe = micro_batt_probe, -+ .remove = micro_batt_remove, -+ .suspend = micro_batt_suspend, -+ .resume = micro_batt_resume, -+}; -+ -+static int micro_batt_init (void) -+{ -+ return platform_driver_register(µ_batt_device_driver); -+} -+ -+static void micro_batt_cleanup (void) -+{ -+ platform_driver_unregister (µ_batt_device_driver); -+} -+ -+module_init (micro_batt_init); -+module_exit (micro_batt_cleanup); -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("gremlin.it"); -+MODULE_DESCRIPTION("driver for iPAQ Atmel micro battery"); -+ -+ -Index: linux-2.6.22/drivers/power/olpc_battery.c -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/drivers/power/olpc_battery.c 2007-08-23 12:13:52.000000000 +0200 -@@ -0,0 +1,302 @@ -+/* -+ * Battery driver for One Laptop Per Child board. -+ * -+ * Copyright © 2006 David Woodhouse <dwmw2@infradead.org> -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include <linux/module.h> -+#include <linux/err.h> -+#include <linux/platform_device.h> -+#include <linux/power_supply.h> -+#include <linux/jiffies.h> -+#include <linux/sched.h> -+#include <asm/io.h> -+ -+#define wBAT_VOLTAGE 0xf900 /* *9.76/32, mV */ -+#define wBAT_CURRENT 0xf902 /* *15.625/120, mA */ -+#define wBAT_TEMP 0xf906 /* *256/1000, °C */ -+#define wAMB_TEMP 0xf908 /* *256/1000, °C */ -+#define SOC 0xf910 /* percentage */ -+#define sMBAT_STATUS 0xfaa4 -+#define sBAT_PRESENT 1 -+#define sBAT_FULL 2 -+#define sBAT_DESTROY 4 /* what is this exactly? */ -+#define sBAT_LOW 32 -+#define sBAT_DISCHG 64 -+#define sMCHARGE_STATUS 0xfaa5 -+#define sBAT_CHARGE 1 -+#define sBAT_OVERTEMP 4 -+#define sBAT_NiMH 8 -+#define sPOWER_FLAG 0xfa40 -+#define ADAPTER_IN 1 -+ -+/********************************************************************* -+ * EC locking and access -+ *********************************************************************/ -+ -+static int lock_ec(void) -+{ -+ unsigned long timeo = jiffies + HZ / 20; -+ -+ while (1) { -+ unsigned char lock = inb(0x6c) & 0x80; -+ if (!lock) -+ return 0; -+ if (time_after(jiffies, timeo)) { -+ printk(KERN_ERR "olpc_battery: failed to lock EC for " -+ "battery access\n"); -+ return 1; -+ } -+ yield(); -+ } -+} -+ -+static void unlock_ec(void) -+{ -+ outb(0xff, 0x6c); -+ return; -+} -+ -+static unsigned char read_ec_byte(unsigned short adr) -+{ -+ outb(adr >> 8, 0x381); -+ outb(adr, 0x382); -+ return inb(0x383); -+} -+ -+static unsigned short read_ec_word(unsigned short adr) -+{ -+ return (read_ec_byte(adr) << 8) | read_ec_byte(adr + 1); -+} -+ -+/********************************************************************* -+ * Power -+ *********************************************************************/ -+ -+static int olpc_ac_get_prop(struct power_supply *psy, -+ enum power_supply_property psp, -+ union power_supply_propval *val) -+{ -+ int ret = 0; -+ -+ if (lock_ec()) -+ return -EIO; -+ -+ switch (psp) { -+ case POWER_SUPPLY_PROP_ONLINE: -+ if (!(read_ec_byte(sMBAT_STATUS) & sBAT_PRESENT)) { -+ ret = -ENODEV; -+ goto out; -+ } -+ val->intval = !!(read_ec_byte(sPOWER_FLAG) & ADAPTER_IN); -+ break; -+ default: -+ ret = -EINVAL; -+ break; -+ } -+out: -+ unlock_ec(); -+ return ret; -+} -+ -+static enum power_supply_property olpc_ac_props[] = { -+ POWER_SUPPLY_PROP_ONLINE, -+}; -+ -+static struct power_supply olpc_ac = { -+ .name = "olpc-ac", -+ .type = POWER_SUPPLY_TYPE_MAINS, -+ .properties = olpc_ac_props, -+ .num_properties = ARRAY_SIZE(olpc_ac_props), -+ .get_property = olpc_ac_get_prop, -+}; -+ -+/********************************************************************* -+ * Battery properties -+ *********************************************************************/ -+ -+static int olpc_bat_get_property(struct power_supply *psy, -+ enum power_supply_property psp, -+ union power_supply_propval *val) -+{ -+ int ret = 0; -+ -+ if (lock_ec()) -+ return -EIO; -+ -+ switch (psp) { -+ case POWER_SUPPLY_PROP_STATUS: -+ { -+ int status = POWER_SUPPLY_STATUS_UNKNOWN; -+ -+ val->intval = read_ec_byte(sMBAT_STATUS); -+ -+ if (!(val->intval & sBAT_PRESENT)) { -+ ret = -ENODEV; -+ goto out; -+ } -+ -+ if (val->intval & sBAT_DISCHG) -+ status = POWER_SUPPLY_STATUS_DISCHARGING; -+ else if (val->intval & sBAT_FULL) -+ status = POWER_SUPPLY_STATUS_FULL; -+ -+ val->intval = read_ec_byte(sMCHARGE_STATUS); -+ if (val->intval & sBAT_CHARGE) -+ status = POWER_SUPPLY_STATUS_CHARGING; -+ -+ val->intval = status; -+ break; -+ } -+ case POWER_SUPPLY_PROP_PRESENT: -+ val->intval = !!(read_ec_byte(sMBAT_STATUS) & sBAT_PRESENT); -+ break; -+ case POWER_SUPPLY_PROP_HEALTH: -+ val->intval = read_ec_byte(sMCHARGE_STATUS); -+ if (val->intval & sBAT_OVERTEMP) -+ val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; -+ else -+ val->intval = POWER_SUPPLY_HEALTH_GOOD; -+ break; -+ case POWER_SUPPLY_PROP_TECHNOLOGY: -+ val->intval = read_ec_byte(sMCHARGE_STATUS); -+ if (val->intval & sBAT_NiMH) -+ val->intval = POWER_SUPPLY_TECHNOLOGY_NIMH; -+ else -+ val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; -+ break; -+ case POWER_SUPPLY_PROP_VOLTAGE_AVG: -+ val->intval = read_ec_byte(wBAT_VOLTAGE) * 9760L / 32; -+ break; -+ case POWER_SUPPLY_PROP_CURRENT_AVG: -+ val->intval = read_ec_byte(wBAT_CURRENT) * 15625L / 120; -+ break; -+ case POWER_SUPPLY_PROP_CAPACITY: -+ val->intval = read_ec_byte(SOC); -+ break; -+ case POWER_SUPPLY_PROP_CAPACITY_LEVEL: -+ val->intval = read_ec_byte(sMBAT_STATUS); -+ if (val->intval & sBAT_FULL) -+ val->intval = POWER_SUPPLY_CAPACITY_LEVEL_FULL; -+ else if (val->intval & sBAT_LOW) -+ val->intval = POWER_SUPPLY_CAPACITY_LEVEL_LOW; -+ else -+ val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; -+ break; -+ case POWER_SUPPLY_PROP_TEMP: -+ val->intval = read_ec_byte(wBAT_TEMP) * 256 / 100; -+ break; -+ case POWER_SUPPLY_PROP_TEMP_AMBIENT: -+ val->intval = read_ec_byte(wAMB_TEMP) * 256 / 100; -+ break; -+ default: -+ ret = -EINVAL; -+ break; -+ } -+ -+out: -+ unlock_ec(); -+ return ret; -+} -+ -+static enum power_supply_property olpc_bat_props[] = { -+ POWER_SUPPLY_PROP_STATUS, -+ POWER_SUPPLY_PROP_PRESENT, -+ POWER_SUPPLY_PROP_HEALTH, -+ POWER_SUPPLY_PROP_TECHNOLOGY, -+ POWER_SUPPLY_PROP_VOLTAGE_AVG, -+ POWER_SUPPLY_PROP_CURRENT_AVG, -+ POWER_SUPPLY_PROP_CAPACITY, -+ POWER_SUPPLY_PROP_CAPACITY_LEVEL, -+ POWER_SUPPLY_PROP_TEMP, -+ POWER_SUPPLY_PROP_TEMP_AMBIENT, -+}; -+ -+/********************************************************************* -+ * Initialisation -+ *********************************************************************/ -+ -+static struct platform_device *bat_pdev; -+ -+static struct power_supply olpc_bat = { -+ .properties = olpc_bat_props, -+ .num_properties = ARRAY_SIZE(olpc_bat_props), -+ .get_property = olpc_bat_get_property, -+ .use_for_apm = 1, -+}; -+ -+static int __init olpc_bat_init(void) -+{ -+ int ret = 0; -+ unsigned short tmp; -+ -+ if (!request_region(0x380, 4, "olpc-battery")) { -+ ret = -EIO; -+ goto region_failed; -+ } -+ -+ if (lock_ec()) { -+ ret = -EIO; -+ goto lock_failed; -+ } -+ -+ tmp = read_ec_word(0xfe92); -+ unlock_ec(); -+ -+ if (tmp != 0x380) { -+ /* Doesn't look like OLPC EC */ -+ ret = -ENODEV; -+ goto not_olpc_ec; -+ } -+ -+ bat_pdev = platform_device_register_simple("olpc-battery", 0, NULL, 0); -+ if (IS_ERR(bat_pdev)) { -+ ret = PTR_ERR(bat_pdev); -+ goto pdev_failed; -+ } -+ -+ ret = power_supply_register(&bat_pdev->dev, &olpc_ac); -+ if (ret) -+ goto ac_failed; -+ -+ olpc_bat.name = bat_pdev->name; -+ -+ ret = power_supply_register(&bat_pdev->dev, &olpc_bat); -+ if (ret) -+ goto battery_failed; -+ -+ goto success; -+ -+battery_failed: -+ power_supply_unregister(&olpc_ac); -+ac_failed: -+ platform_device_unregister(bat_pdev); -+pdev_failed: -+not_olpc_ec: -+lock_failed: -+ release_region(0x380, 4); -+region_failed: -+success: -+ return ret; -+} -+ -+static void __exit olpc_bat_exit(void) -+{ -+ power_supply_unregister(&olpc_bat); -+ power_supply_unregister(&olpc_ac); -+ platform_device_unregister(bat_pdev); -+ release_region(0x380, 4); -+ return; -+} -+ -+module_init(olpc_bat_init); -+module_exit(olpc_bat_exit); -+ -+MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("Battery driver for One Laptop Per Child " -+ "($100 laptop) board."); -Index: linux-2.6.22/drivers/power/pda_power.c -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/drivers/power/pda_power.c 2007-08-23 12:13:52.000000000 +0200 -@@ -0,0 +1,263 @@ -+/* -+ * Common power driver for PDAs and phones with one or two external -+ * power supplies (AC/USB) connected to main and backup batteries, -+ * and optional builtin charger. -+ * -+ * Copyright 2007 Anton Vorontsov <cbou@mail.ru> -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include <linux/module.h> -+#include <linux/platform_device.h> -+#include <linux/interrupt.h> -+#include <linux/power_supply.h> -+#include <linux/pda_power.h> -+#include <linux/timer.h> -+#include <linux/jiffies.h> -+ -+static inline unsigned int get_irq_flags(struct resource *res) -+{ -+ unsigned int flags = IRQF_DISABLED | IRQF_SHARED; -+ -+ flags |= res->flags & IRQF_TRIGGER_MASK; -+ -+ return flags; -+} -+ -+static struct device *dev; -+static struct pda_power_pdata *pdata; -+static struct resource *ac_irq, *usb_irq; -+static struct timer_list charger_timer; -+static struct timer_list supply_timer; -+ -+static int pda_power_get_property(struct power_supply *psy, -+ enum power_supply_property psp, -+ union power_supply_propval *val) -+{ -+ switch (psp) { -+ case POWER_SUPPLY_PROP_ONLINE: -+ if (psy->type == POWER_SUPPLY_TYPE_MAINS) -+ val->intval = pdata->is_ac_online ? -+ pdata->is_ac_online() : 0; -+ else -+ val->intval = pdata->is_usb_online ? -+ pdata->is_usb_online() : 0; -+ break; -+ default: -+ return -EINVAL; -+ } -+ return 0; -+} -+ -+static enum power_supply_property pda_power_props[] = { -+ POWER_SUPPLY_PROP_ONLINE, -+}; -+ -+static char *pda_power_supplied_to[] = { -+ "main-battery", -+ "backup-battery", -+}; -+ -+static struct power_supply pda_power_supplies[] = { -+ { -+ .name = "ac", -+ .type = POWER_SUPPLY_TYPE_MAINS, -+ .supplied_to = pda_power_supplied_to, -+ .num_supplicants = ARRAY_SIZE(pda_power_supplied_to), -+ .properties = pda_power_props, -+ .num_properties = ARRAY_SIZE(pda_power_props), -+ .get_property = pda_power_get_property, -+ }, -+ { -+ .name = "usb", -+ .type = POWER_SUPPLY_TYPE_USB, -+ .supplied_to = pda_power_supplied_to, -+ .num_supplicants = ARRAY_SIZE(pda_power_supplied_to), -+ .properties = pda_power_props, -+ .num_properties = ARRAY_SIZE(pda_power_props), -+ .get_property = pda_power_get_property, -+ }, -+}; -+ -+static void update_charger(void) -+{ -+ if (!pdata->set_charge) -+ return; -+ -+ if (pdata->is_ac_online && pdata->is_ac_online()) { -+ dev_dbg(dev, "charger on (AC)\n"); -+ pdata->set_charge(PDA_POWER_CHARGE_AC); -+ } -+ else if (pdata->is_usb_online && pdata->is_usb_online()) { -+ dev_dbg(dev, "charger on (USB)\n"); -+ pdata->set_charge(PDA_POWER_CHARGE_USB); -+ } -+ else { -+ dev_dbg(dev, "charger off\n"); -+ pdata->set_charge(0); -+ } -+ -+ return; -+} -+ -+static void supply_timer_func(unsigned long irq) -+{ -+ if (ac_irq && irq == ac_irq->start) -+ power_supply_changed(&pda_power_supplies[0]); -+ else if (usb_irq && irq == usb_irq->start) -+ power_supply_changed(&pda_power_supplies[1]); -+ return; -+} -+ -+static void charger_timer_func(unsigned long irq) -+{ -+ update_charger(); -+ -+ /* Okay, charger set. Now wait a bit before notifying supplicants, -+ * charge power should stabilize. */ -+ supply_timer.data = irq; -+ mod_timer(&supply_timer, -+ jiffies + msecs_to_jiffies(pdata->wait_for_charger)); -+ return; -+} -+ -+static irqreturn_t power_changed_isr(int irq, void *unused) -+{ -+ /* Wait a bit before reading ac/usb line status and setting charger, -+ * because ac/usb status readings may lag from irq. */ -+ charger_timer.data = irq; -+ mod_timer(&charger_timer, -+ jiffies + msecs_to_jiffies(pdata->wait_for_status)); -+ return IRQ_HANDLED; -+} -+ -+static int pda_power_probe(struct platform_device *pdev) -+{ -+ int ret = 0; -+ -+ dev = &pdev->dev; -+ -+ if (pdev->id != -1) { -+ dev_err(dev, "it's meaningless to register several " -+ "pda_powers, use id = -1\n"); -+ ret = -EINVAL; -+ goto wrongid; -+ } -+ -+ pdata = pdev->dev.platform_data; -+ -+ update_charger(); -+ -+ if (!pdata->wait_for_status) -+ pdata->wait_for_status = 500; -+ -+ if (!pdata->wait_for_charger) -+ pdata->wait_for_charger = 500; -+ -+ setup_timer(&charger_timer, charger_timer_func, 0); -+ setup_timer(&supply_timer, supply_timer_func, 0); -+ -+ ac_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "ac"); -+ usb_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "usb"); -+ if (!ac_irq && !usb_irq) { -+ dev_err(dev, "no ac/usb irq specified\n"); -+ ret = -ENODEV; -+ goto noirqs; -+ } -+ -+ if (pdata->supplied_to) { -+ pda_power_supplies[0].supplied_to = pdata->supplied_to; -+ pda_power_supplies[1].supplied_to = pdata->supplied_to; -+ pda_power_supplies[0].num_supplicants = pdata->num_supplicants; -+ pda_power_supplies[1].num_supplicants = pdata->num_supplicants; -+ } -+ -+ ret = power_supply_register(&pdev->dev, &pda_power_supplies[0]); -+ if (ret) { -+ dev_err(dev, "failed to register %s power supply\n", -+ pda_power_supplies[0].name); -+ goto supply0_failed; -+ } -+ -+ ret = power_supply_register(&pdev->dev, &pda_power_supplies[1]); -+ if (ret) { -+ dev_err(dev, "failed to register %s power supply\n", -+ pda_power_supplies[1].name); -+ goto supply1_failed; -+ } -+ -+ if (ac_irq) { -+ ret = request_irq(ac_irq->start, power_changed_isr, -+ get_irq_flags(ac_irq), ac_irq->name, -+ &pda_power_supplies[0]); -+ if (ret) { -+ dev_err(dev, "request ac irq failed\n"); -+ goto ac_irq_failed; -+ } -+ } -+ -+ if (usb_irq) { -+ ret = request_irq(usb_irq->start, power_changed_isr, -+ get_irq_flags(usb_irq), usb_irq->name, -+ &pda_power_supplies[1]); -+ if (ret) { -+ dev_err(dev, "request usb irq failed\n"); -+ goto usb_irq_failed; -+ } -+ } -+ -+ goto success; -+ -+usb_irq_failed: -+ if (ac_irq) -+ free_irq(ac_irq->start, &pda_power_supplies[0]); -+ac_irq_failed: -+ power_supply_unregister(&pda_power_supplies[1]); -+supply1_failed: -+ power_supply_unregister(&pda_power_supplies[0]); -+supply0_failed: -+noirqs: -+wrongid: -+success: -+ return ret; -+} -+ -+static int pda_power_remove(struct platform_device *pdev) -+{ -+ if (usb_irq) -+ free_irq(usb_irq->start, &pda_power_supplies[1]); -+ if (ac_irq) -+ free_irq(ac_irq->start, &pda_power_supplies[0]); -+ del_timer_sync(&charger_timer); -+ del_timer_sync(&supply_timer); -+ power_supply_unregister(&pda_power_supplies[1]); -+ power_supply_unregister(&pda_power_supplies[0]); -+ return 0; -+} -+ -+static struct platform_driver pda_power_pdrv = { -+ .driver = { -+ .name = "pda-power", -+ }, -+ .probe = pda_power_probe, -+ .remove = pda_power_remove, -+}; -+ -+static int __init pda_power_init(void) -+{ -+ return platform_driver_register(&pda_power_pdrv); -+} -+ -+static void __exit pda_power_exit(void) -+{ -+ platform_driver_unregister(&pda_power_pdrv); -+ return; -+} -+ -+module_init(pda_power_init); -+module_exit(pda_power_exit); -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Anton Vorontsov <cbou@mail.ru>"); -Index: linux-2.6.22/drivers/power/pmu_battery.c -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/drivers/power/pmu_battery.c 2007-08-23 12:13:52.000000000 +0200 -@@ -0,0 +1,215 @@ -+/* -+ * Battery class driver for Apple PMU -+ * -+ * Copyright © 2006 David Woodhouse <dwmw2@infradead.org> -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include <linux/module.h> -+#include <linux/platform_device.h> -+#include <linux/err.h> -+#include <linux/power_supply.h> -+#include <linux/adb.h> -+#include <linux/pmu.h> -+ -+static struct pmu_battery_dev { -+ struct power_supply bat; -+ struct pmu_battery_info *pbi; -+ char name[16]; -+ int propval; -+} *pbats[PMU_MAX_BATTERIES]; -+ -+#define to_pmu_battery_dev(x) container_of(x, struct pmu_battery_dev, bat) -+ -+/********************************************************************* -+ * Power -+ *********************************************************************/ -+ -+static int pmu_get_ac_prop(struct power_supply *psy, -+ enum power_supply_property psp, -+ union power_supply_propval *val) -+{ -+ switch (psp) { -+ case POWER_SUPPLY_PROP_ONLINE: -+ val->intval = (!!(pmu_power_flags & PMU_PWR_AC_PRESENT)) || -+ (pmu_battery_count == 0); -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static enum power_supply_property pmu_ac_props[] = { -+ POWER_SUPPLY_PROP_ONLINE, -+}; -+ -+static struct power_supply pmu_ac = { -+ .name = "pmu-ac", -+ .type = POWER_SUPPLY_TYPE_MAINS, -+ .properties = pmu_ac_props, -+ .num_properties = ARRAY_SIZE(pmu_ac_props), -+ .get_property = pmu_get_ac_prop, -+}; -+ -+/********************************************************************* -+ * Battery properties -+ *********************************************************************/ -+ -+static char *pmu_batt_types[] = { -+ "Smart", "Comet", "Hooper", "Unknown" -+}; -+ -+static char *pmu_bat_get_model_name(struct pmu_battery_info *pbi) -+{ -+ switch (pbi->flags & PMU_BATT_TYPE_MASK) { -+ case PMU_BATT_TYPE_SMART: -+ return pmu_batt_types[0]; -+ case PMU_BATT_TYPE_COMET: -+ return pmu_batt_types[1]; -+ case PMU_BATT_TYPE_HOOPER: -+ return pmu_batt_types[2]; -+ default: break; -+ } -+ return pmu_batt_types[3]; -+} -+ -+static int pmu_bat_get_property(struct power_supply *psy, -+ enum power_supply_property psp, -+ union power_supply_propval *val) -+{ -+ struct pmu_battery_dev *pbat = to_pmu_battery_dev(psy); -+ struct pmu_battery_info *pbi = pbat->pbi; -+ -+ switch (psp) { -+ case POWER_SUPPLY_PROP_STATUS: -+ if (pbi->flags & PMU_BATT_CHARGING) -+ val->intval = POWER_SUPPLY_STATUS_CHARGING; -+ else -+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING; -+ break; -+ case POWER_SUPPLY_PROP_PRESENT: -+ val->intval = !!(pbi->flags & PMU_BATT_PRESENT); -+ break; -+ case POWER_SUPPLY_PROP_MODEL_NAME: -+ val->strval = pmu_bat_get_model_name(pbi); -+ break; -+ case POWER_SUPPLY_PROP_ENERGY_AVG: -+ val->intval = pbi->charge * 1000; /* mWh -> µWh */ -+ break; -+ case POWER_SUPPLY_PROP_ENERGY_FULL: -+ val->intval = pbi->max_charge * 1000; /* mWh -> µWh */ -+ break; -+ case POWER_SUPPLY_PROP_CURRENT_AVG: -+ val->intval = pbi->amperage * 1000; /* mA -> µA */ -+ break; -+ case POWER_SUPPLY_PROP_VOLTAGE_AVG: -+ val->intval = pbi->voltage * 1000; /* mV -> µV */ -+ break; -+ case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: -+ val->intval = pbi->time_remaining; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static enum power_supply_property pmu_bat_props[] = { -+ POWER_SUPPLY_PROP_STATUS, -+ POWER_SUPPLY_PROP_PRESENT, -+ POWER_SUPPLY_PROP_MODEL_NAME, -+ POWER_SUPPLY_PROP_ENERGY_AVG, -+ POWER_SUPPLY_PROP_ENERGY_FULL, -+ POWER_SUPPLY_PROP_CURRENT_AVG, -+ POWER_SUPPLY_PROP_VOLTAGE_AVG, -+ POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, -+}; -+ -+/********************************************************************* -+ * Initialisation -+ *********************************************************************/ -+ -+static struct platform_device *bat_pdev; -+ -+static int __init pmu_bat_init(void) -+{ -+ int ret; -+ int i; -+ -+ bat_pdev = platform_device_register_simple("pmu-battery", -+ 0, NULL, 0); -+ if (IS_ERR(bat_pdev)) { -+ ret = PTR_ERR(bat_pdev); -+ goto pdev_register_failed; -+ } -+ -+ ret = power_supply_register(&bat_pdev->dev, &pmu_ac); -+ if (ret) -+ goto ac_register_failed; -+ -+ for (i = 0; i < pmu_battery_count; i++) { -+ struct pmu_battery_dev *pbat = kzalloc(sizeof(*pbat), -+ GFP_KERNEL); -+ if (!pbat) -+ break; -+ -+ sprintf(pbat->name, "PMU battery %d", i); -+ pbat->bat.name = pbat->name; -+ pbat->bat.properties = pmu_bat_props; -+ pbat->bat.num_properties = ARRAY_SIZE(pmu_bat_props); -+ pbat->bat.get_property = pmu_bat_get_property; -+ pbat->pbi = &pmu_batteries[i]; -+ -+ ret = power_supply_register(&bat_pdev->dev, &pbat->bat); -+ if (ret) { -+ kfree(pbat); -+ goto battery_register_failed; -+ } -+ pbats[i] = pbat; -+ } -+ -+ goto success; -+ -+battery_register_failed: -+ while (i--) { -+ if (!pbats[i]) -+ continue; -+ power_supply_unregister(&pbats[i]->bat); -+ kfree(pbats[i]); -+ } -+ power_supply_unregister(&pmu_ac); -+ac_register_failed: -+ platform_device_unregister(bat_pdev); -+pdev_register_failed: -+success: -+ return ret; -+} -+ -+static void __exit pmu_bat_exit(void) -+{ -+ int i; -+ -+ for (i = 0; i < PMU_MAX_BATTERIES; i++) { -+ if (!pbats[i]) -+ continue; -+ power_supply_unregister(&pbats[i]->bat); -+ kfree(pbats[i]); -+ } -+ power_supply_unregister(&pmu_ac); -+ platform_device_unregister(bat_pdev); -+ -+ return; -+} -+ -+module_init(pmu_bat_init); -+module_exit(pmu_bat_exit); -+ -+MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("PMU battery driver"); -Index: linux-2.6.22/drivers/power/power_supply_core.c -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/drivers/power/power_supply_core.c 2007-08-23 12:13:52.000000000 +0200 -@@ -0,0 +1,168 @@ -+/* -+ * Universal power supply monitor class -+ * -+ * Copyright (c) 2007 Anton Vorontsov <cbou@mail.ru> -+ * Copyright (c) 2004 Szabolcs Gyurko -+ * Copyright (c) 2003 Ian Molton <spyro@f2s.com> -+ * -+ * Modified: 2004, Oct Szabolcs Gyurko -+ * -+ * You may use this code as per GPL version 2 -+ */ -+ -+#include <linux/module.h> -+#include <linux/types.h> -+#include <linux/init.h> -+#include <linux/device.h> -+#include <linux/err.h> -+#include <linux/power_supply.h> -+#include "power_supply.h" -+ -+struct class *power_supply_class; -+ -+static void power_supply_changed_work(struct work_struct *work) -+{ -+ struct power_supply *psy = container_of(work, struct power_supply, -+ changed_work); -+ int i; -+ -+ dev_dbg(psy->dev, "%s\n", __FUNCTION__); -+ -+ for (i = 0; i < psy->num_supplicants; i++) { -+ struct device *dev; -+ -+ down(&power_supply_class->sem); -+ list_for_each_entry(dev, &power_supply_class->devices, node) { -+ struct power_supply *pst = dev_get_drvdata(dev); -+ -+ if (!strcmp(psy->supplied_to[i], pst->name)) { -+ if (pst->external_power_changed) -+ pst->external_power_changed(pst); -+ } -+ } -+ up(&power_supply_class->sem); -+ } -+ -+ power_supply_update_leds(psy); -+ -+ kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE); -+ -+ return; -+} -+ -+void power_supply_changed(struct power_supply *psy) -+{ -+ dev_dbg(psy->dev, "%s\n", __FUNCTION__); -+ -+ schedule_work(&psy->changed_work); -+ -+ return; -+} -+ -+int power_supply_am_i_supplied(struct power_supply *psy) -+{ -+ union power_supply_propval ret = {0,}; -+ struct device *dev; -+ -+ down(&power_supply_class->sem); -+ list_for_each_entry(dev, &power_supply_class->devices, node) { -+ struct power_supply *epsy = dev_get_drvdata(dev); -+ int i; -+ -+ for (i = 0; i < epsy->num_supplicants; i++) { -+ if (!strcmp(epsy->supplied_to[i], psy->name)) { -+ if (epsy->get_property(epsy, -+ POWER_SUPPLY_PROP_ONLINE, &ret)) -+ continue; -+ if (ret.intval) -+ goto out; -+ } -+ } -+ } -+out: -+ up(&power_supply_class->sem); -+ -+ dev_dbg(psy->dev, "%s %d\n", __FUNCTION__, ret.intval); -+ -+ return ret.intval; -+} -+ -+int power_supply_register(struct device *parent, struct power_supply *psy) -+{ -+ int rc = 0; -+ -+ psy->dev = device_create(power_supply_class, parent, 0, -+ "%s", psy->name); -+ if (IS_ERR(psy->dev)) { -+ rc = PTR_ERR(psy->dev); -+ goto dev_create_failed; -+ } -+ -+ dev_set_drvdata(psy->dev, psy); -+ -+ INIT_WORK(&psy->changed_work, power_supply_changed_work); -+ -+ rc = power_supply_create_attrs(psy); -+ if (rc) -+ goto create_attrs_failed; -+ -+ rc = power_supply_create_triggers(psy); -+ if (rc) -+ goto create_triggers_failed; -+ -+ power_supply_changed(psy); -+ -+ goto success; -+ -+create_triggers_failed: -+ power_supply_remove_attrs(psy); -+create_attrs_failed: -+ device_unregister(psy->dev); -+dev_create_failed: -+success: -+ return rc; -+} -+ -+void power_supply_unregister(struct power_supply *psy) -+{ -+ flush_scheduled_work(); -+ power_supply_remove_triggers(psy); -+ power_supply_remove_attrs(psy); -+ device_unregister(psy->dev); -+ return; -+} -+ -+static int __init power_supply_class_init(void) -+{ -+ power_supply_class = class_create(THIS_MODULE, "power_supply"); -+ -+ if (IS_ERR(power_supply_class)) -+ return PTR_ERR(power_supply_class); -+ -+ power_supply_class->dev_uevent = power_supply_uevent; -+ -+ return 0; -+} -+ -+static void __exit power_supply_class_exit(void) -+{ -+ class_destroy(power_supply_class); -+ return; -+} -+ -+EXPORT_SYMBOL_GPL(power_supply_changed); -+EXPORT_SYMBOL_GPL(power_supply_am_i_supplied); -+EXPORT_SYMBOL_GPL(power_supply_register); -+EXPORT_SYMBOL_GPL(power_supply_unregister); -+ -+/* exported for the APM Power driver, APM emulation */ -+EXPORT_SYMBOL_GPL(power_supply_class); -+ -+subsys_initcall(power_supply_class_init); -+module_exit(power_supply_class_exit); -+ -+MODULE_DESCRIPTION("Universal power supply monitor class"); -+MODULE_AUTHOR("Ian Molton <spyro@f2s.com>, " -+ "Szabolcs Gyurko, " -+ "Anton Vorontsov <cbou@mail.ru>"); -+MODULE_LICENSE("GPL"); -Index: linux-2.6.22/drivers/power/power_supply.h -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/drivers/power/power_supply.h 2007-08-23 12:13:52.000000000 +0200 -@@ -0,0 +1,42 @@ -+/* -+ * Functions private to power supply class -+ * -+ * Copyright (c) 2007 Anton Vorontsov <cbou@mail.ru> -+ * Copyright (c) 2004 Szabolcs Gyurko -+ * Copyright (c) 2003 Ian Molton <spyro@f2s.com> -+ * -+ * Modified: 2004, Oct Szabolcs Gyurko -+ * -+ * You may use this code as per GPL version 2 -+ */ -+ -+#ifdef CONFIG_SYSFS -+ -+extern int power_supply_create_attrs(struct power_supply *psy); -+extern void power_supply_remove_attrs(struct power_supply *psy); -+extern int power_supply_uevent(struct device *dev, char **envp, int num_envp, -+ char *buffer, int buffer_size); -+ -+#else -+ -+static inline int power_supply_create_attrs(struct power_supply *psy) -+{ return 0; } -+static inline void power_supply_remove_attrs(struct power_supply *psy) {} -+#define power_supply_uevent NULL -+ -+#endif /* CONFIG_SYSFS */ -+ -+#ifdef CONFIG_LEDS_TRIGGERS -+ -+extern void power_supply_update_leds(struct power_supply *psy); -+extern int power_supply_create_triggers(struct power_supply *psy); -+extern void power_supply_remove_triggers(struct power_supply *psy); -+ -+#else -+ -+static inline void power_supply_update_leds(struct power_supply *psy) {} -+static inline int power_supply_create_triggers(struct power_supply *psy) -+{ return 0; } -+static inline void power_supply_remove_triggers(struct power_supply *psy) {} -+ -+#endif /* CONFIG_LEDS_TRIGGERS */ -Index: linux-2.6.22/drivers/power/power_supply_leds.c -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/drivers/power/power_supply_leds.c 2007-08-23 12:13:52.000000000 +0200 -@@ -0,0 +1,188 @@ -+/* -+ * LEDs triggers for power supply class -+ * -+ * Copyright (c) 2007 Anton Vorontsov <cbou@mail.ru> -+ * Copyright (c) 2004 Szabolcs Gyurko -+ * Copyright (c) 2003 Ian Molton <spyro@f2s.com> -+ * -+ * Modified: 2004, Oct Szabolcs Gyurko -+ * -+ * You may use this code as per GPL version 2 -+ */ -+ -+#include <linux/power_supply.h> -+ -+/* If we have hwtimer trigger, then use it to blink charging LED */ -+ -+#if defined(CONFIG_LEDS_TRIGGER_HWTIMER) || \ -+ (defined(CONFIG_BATTERY_MODULE) && \ -+ defined(CONFIG_LEDS_TRIGGER_HWTIMER_MODULE)) -+ #define led_trigger_register_charging led_trigger_register_hwtimer -+ #define led_trigger_unregister_charging led_trigger_unregister_hwtimer -+#else -+ #define led_trigger_register_charging led_trigger_register_simple -+ #define led_trigger_unregister_charging led_trigger_unregister_simple -+#endif -+ -+/* Battery specific LEDs triggers. */ -+ -+static void power_supply_update_bat_leds(struct power_supply *psy) -+{ -+ union power_supply_propval status; -+ -+ if (psy->get_property(psy, POWER_SUPPLY_PROP_STATUS, &status)) -+ return; -+ -+ dev_dbg(psy->dev, "%s %d\n", __FUNCTION__, status.intval); -+ -+ switch(status.intval) { -+ case POWER_SUPPLY_STATUS_FULL: -+ led_trigger_event(psy->charging_full_trig, LED_FULL); -+ led_trigger_event(psy->charging_trig, LED_OFF); -+ led_trigger_event(psy->full_trig, LED_FULL); -+ break; -+ case POWER_SUPPLY_STATUS_CHARGING: -+ led_trigger_event(psy->charging_full_trig, LED_FULL); -+ led_trigger_event(psy->charging_trig, LED_FULL); -+ led_trigger_event(psy->full_trig, LED_OFF); -+ break; -+ default: -+ led_trigger_event(psy->charging_full_trig, LED_OFF); -+ led_trigger_event(psy->charging_trig, LED_OFF); -+ led_trigger_event(psy->full_trig, LED_OFF); -+ break; -+ } -+ -+ return; -+} -+ -+static int power_supply_create_bat_triggers(struct power_supply *psy) -+{ -+ int rc = 0; -+ -+ psy->charging_full_trig_name = kmalloc(strlen(psy->name) + -+ sizeof("-charging-or-full"), GFP_KERNEL); -+ if (!psy->charging_full_trig_name) -+ goto charging_full_failed; -+ -+ psy->charging_trig_name = kmalloc(strlen(psy->name) + -+ sizeof("-charging"), GFP_KERNEL); -+ if (!psy->charging_trig_name) -+ goto charging_failed; -+ -+ psy->full_trig_name = kmalloc(strlen(psy->name) + -+ sizeof("-full"), GFP_KERNEL); -+ if (!psy->full_trig_name) -+ goto full_failed; -+ -+ strcpy(psy->charging_full_trig_name, psy->name); -+ strcat(psy->charging_full_trig_name, "-charging-or-full"); -+ strcpy(psy->charging_trig_name, psy->name); -+ strcat(psy->charging_trig_name, "-charging"); -+ strcpy(psy->full_trig_name, psy->name); -+ strcat(psy->full_trig_name, "-full"); -+ -+ led_trigger_register_simple(psy->charging_full_trig_name, -+ &psy->charging_full_trig); -+ led_trigger_register_charging(psy->charging_trig_name, -+ &psy->charging_trig); -+ led_trigger_register_simple(psy->full_trig_name, -+ &psy->full_trig); -+ -+ goto success; -+ -+full_failed: -+ kfree(psy->charging_trig_name); -+charging_failed: -+ kfree(psy->charging_full_trig_name); -+charging_full_failed: -+ rc = -ENOMEM; -+success: -+ return rc; -+} -+ -+static void power_supply_remove_bat_triggers(struct power_supply *psy) -+{ -+ led_trigger_unregister_simple(psy->charging_full_trig); -+ led_trigger_unregister_charging(psy->charging_trig); -+ led_trigger_unregister_simple(psy->full_trig); -+ kfree(psy->full_trig_name); -+ kfree(psy->charging_trig_name); -+ kfree(psy->charging_full_trig_name); -+ return; -+} -+ -+/* Generated power specific LEDs triggers. */ -+ -+static void power_supply_update_gen_leds(struct power_supply *psy) -+{ -+ union power_supply_propval online; -+ -+ if (psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &online)) -+ return; -+ -+ dev_dbg(psy->dev, "%s %d\n", __FUNCTION__, online.intval); -+ -+ if (online.intval) -+ led_trigger_event(psy->online_trig, LED_FULL); -+ else -+ led_trigger_event(psy->online_trig, LED_OFF); -+ -+ return; -+} -+ -+static int power_supply_create_gen_triggers(struct power_supply *psy) -+{ -+ int rc = 0; -+ -+ psy->online_trig_name = kmalloc(strlen(psy->name) + sizeof("-online"), -+ GFP_KERNEL); -+ if (!psy->online_trig_name) -+ goto online_failed; -+ -+ strcpy(psy->online_trig_name, psy->name); -+ strcat(psy->online_trig_name, "-online"); -+ -+ led_trigger_register_simple(psy->online_trig_name, &psy->online_trig); -+ -+ goto success; -+ -+online_failed: -+ rc = -ENOMEM; -+success: -+ return rc; -+} -+ -+static void power_supply_remove_gen_triggers(struct power_supply *psy) -+{ -+ led_trigger_unregister_simple(psy->online_trig); -+ kfree(psy->online_trig_name); -+ return; -+} -+ -+/* Choice what triggers to create&update. */ -+ -+void power_supply_update_leds(struct power_supply *psy) -+{ -+ if (psy->type == POWER_SUPPLY_TYPE_BATTERY) -+ power_supply_update_bat_leds(psy); -+ else -+ power_supply_update_gen_leds(psy); -+ return; -+} -+ -+int power_supply_create_triggers(struct power_supply *psy) -+{ -+ if (psy->type == POWER_SUPPLY_TYPE_BATTERY) -+ return power_supply_create_bat_triggers(psy); -+ return power_supply_create_gen_triggers(psy); -+} -+ -+void power_supply_remove_triggers(struct power_supply *psy) -+{ -+ if (psy->type == POWER_SUPPLY_TYPE_BATTERY) -+ power_supply_remove_bat_triggers(psy); -+ else -+ power_supply_remove_gen_triggers(psy); -+ return; -+} -Index: linux-2.6.22/drivers/power/power_supply_sysfs.c -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/drivers/power/power_supply_sysfs.c 2007-08-23 12:13:52.000000000 +0200 -@@ -0,0 +1,289 @@ -+/* -+ * Sysfs interface for the universal power supply monitor class -+ * -+ * Copyright © 2007 David Woodhouse <dwmw2@infradead.org> -+ * Copyright (c) 2007 Anton Vorontsov <cbou@mail.ru> -+ * Copyright (c) 2004 Szabolcs Gyurko -+ * Copyright (c) 2003 Ian Molton <spyro@f2s.com> -+ * -+ * Modified: 2004, Oct Szabolcs Gyurko -+ * -+ * You may use this code as per GPL version 2 -+ */ -+ -+#include <linux/ctype.h> -+#include <linux/power_supply.h> -+ -+/* -+ * This is because the name "current" breaks the device attr macro. -+ * The "current" word resolvs to "(get_current())" so instead of -+ * "current" "(get_current())" appears in the sysfs. -+ * -+ * The source of this definition is the device.h which calls __ATTR -+ * macro in sysfs.h which calls the __stringify macro. -+ * -+ * Only modification that the name is not tried to be resolved -+ * (as a macro let's say). -+ */ -+ -+#define POWER_SUPPLY_ATTR(_name) \ -+{ \ -+ .attr = { .name = #_name, .mode = 0444, .owner = THIS_MODULE }, \ -+ .show = power_supply_show_property, \ -+ .store = NULL, \ -+} -+ -+static struct device_attribute power_supply_attrs[]; -+ -+static ssize_t power_supply_show_property(struct device *dev, -+ struct device_attribute *attr, -+ char *buf) { -+ static char *status_text[] = { -+ "Unknown", "Charging", "Discharging", "Not charging", "Full" -+ }; -+ static char *health_text[] = { -+ "Unknown", "Good", "Overheat", "Dead" -+ }; -+ static char *technology_text[] = { -+ "Unknown", "NiMH", "Li-ion", "Li-poly" -+ }; -+ static char *capacity_level_text[] = { -+ "Unknown", "Critical", "Low", "Normal", "High", "Full" -+ }; -+ ssize_t ret; -+ struct power_supply *psy = dev_get_drvdata(dev); -+ const ptrdiff_t off = attr - power_supply_attrs; -+ union power_supply_propval value; -+ -+ ret = psy->get_property(psy, off, &value); -+ -+ if (ret < 0) { -+ dev_err(dev, "driver failed to report `%s' property\n", -+ attr->attr.name); -+ return ret; -+ } -+ -+ if (off == POWER_SUPPLY_PROP_STATUS) -+ return sprintf(buf, "%s\n", status_text[value.intval]); -+ else if (off == POWER_SUPPLY_PROP_HEALTH) -+ return sprintf(buf, "%s\n", health_text[value.intval]); -+ else if (off == POWER_SUPPLY_PROP_TECHNOLOGY) -+ return sprintf(buf, "%s\n", technology_text[value.intval]); -+ else if (off == POWER_SUPPLY_PROP_CAPACITY_LEVEL) -+ return sprintf(buf, "%s\n", -+ capacity_level_text[value.intval]); -+ else if (off == POWER_SUPPLY_PROP_MODEL_NAME) -+ return sprintf(buf, "%s\n", value.strval); -+ -+ return sprintf(buf, "%d\n", value.intval); -+} -+ -+/* Must be in the same order as POWER_SUPPLY_PROP_* */ -+static struct device_attribute power_supply_attrs[] = { -+ /* Properties of type `int' */ -+ POWER_SUPPLY_ATTR(status), -+ POWER_SUPPLY_ATTR(health), -+ POWER_SUPPLY_ATTR(present), -+ POWER_SUPPLY_ATTR(online), -+ POWER_SUPPLY_ATTR(technology), -+ POWER_SUPPLY_ATTR(voltage_max_design), -+ POWER_SUPPLY_ATTR(voltage_min_design), -+ POWER_SUPPLY_ATTR(voltage_now), -+ POWER_SUPPLY_ATTR(voltage_avg), -+ POWER_SUPPLY_ATTR(current_now), -+ POWER_SUPPLY_ATTR(current_avg), -+ POWER_SUPPLY_ATTR(charge_full_design), -+ POWER_SUPPLY_ATTR(charge_empty_design), -+ POWER_SUPPLY_ATTR(charge_full), -+ POWER_SUPPLY_ATTR(charge_empty), -+ POWER_SUPPLY_ATTR(charge_now), -+ POWER_SUPPLY_ATTR(charge_avg), -+ POWER_SUPPLY_ATTR(energy_full_design), -+ POWER_SUPPLY_ATTR(energy_empty_design), -+ POWER_SUPPLY_ATTR(energy_full), -+ POWER_SUPPLY_ATTR(energy_empty), -+ POWER_SUPPLY_ATTR(energy_now), -+ POWER_SUPPLY_ATTR(energy_avg), -+ POWER_SUPPLY_ATTR(capacity), -+ POWER_SUPPLY_ATTR(capacity_level), -+ POWER_SUPPLY_ATTR(temp), -+ POWER_SUPPLY_ATTR(temp_ambient), -+ POWER_SUPPLY_ATTR(time_to_empty_now), -+ POWER_SUPPLY_ATTR(time_to_empty_avg), -+ POWER_SUPPLY_ATTR(time_to_full_now), -+ POWER_SUPPLY_ATTR(time_to_full_avg), -+ /* Properties of type `const char *' */ -+ POWER_SUPPLY_ATTR(model_name), -+}; -+ -+static ssize_t power_supply_show_static_attrs(struct device *dev, -+ struct device_attribute *attr, -+ char *buf) { -+ static char *type_text[] = { "Battery", "UPS", "Mains", "USB" }; -+ struct power_supply *psy = dev_get_drvdata(dev); -+ -+ return sprintf(buf, "%s\n", type_text[psy->type]); -+} -+ -+static struct device_attribute power_supply_static_attrs[] = { -+ __ATTR(type, 0444, power_supply_show_static_attrs, NULL), -+}; -+ -+int power_supply_create_attrs(struct power_supply *psy) -+{ -+ int rc = 0; -+ int i, j; -+ -+ for (i = 0; i < ARRAY_SIZE(power_supply_static_attrs); i++) { -+ rc = device_create_file(psy->dev, -+ &power_supply_static_attrs[i]); -+ if (rc) -+ goto statics_failed; -+ } -+ -+ for (j = 0; j < psy->num_properties; j++) { -+ rc = device_create_file(psy->dev, -+ &power_supply_attrs[psy->properties[j]]); -+ if (rc) -+ goto dynamics_failed; -+ } -+ -+ goto succeed; -+ -+dynamics_failed: -+ while (j--) -+ device_remove_file(psy->dev, -+ &power_supply_attrs[psy->properties[j]]); -+statics_failed: -+ while (i--) -+ device_remove_file(psy->dev, -+ &power_supply_static_attrs[psy->properties[i]]); -+succeed: -+ return rc; -+} -+ -+void power_supply_remove_attrs(struct power_supply *psy) -+{ -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(power_supply_static_attrs); i++) -+ device_remove_file(psy->dev, -+ &power_supply_static_attrs[i]); -+ -+ for (i = 0; i < psy->num_properties; i++) -+ device_remove_file(psy->dev, -+ &power_supply_attrs[psy->properties[i]]); -+ -+ return; -+} -+ -+static char *kstruprdup(const char *str, gfp_t gfp) -+{ -+ char *ret, *ustr; -+ -+ ustr = ret = kmalloc(strlen(str) + 1, gfp); -+ -+ if (!ret) -+ return NULL; -+ -+ while (*str) -+ *ustr++ = toupper(*str++); -+ -+ *ustr = 0; -+ -+ return ret; -+} -+ -+int power_supply_uevent(struct device *dev, char **envp, int num_envp, -+ char *buffer, int buffer_size) -+{ -+ struct power_supply *psy = dev_get_drvdata(dev); -+ int i = 0, length = 0, ret = 0, j; -+ char *prop_buf; -+ char *attrname; -+ -+ dev_dbg(dev, "uevent\n"); -+ -+ if (!psy) { -+ dev_dbg(dev, "No power supply yet\n"); -+ return ret; -+ } -+ -+ dev_dbg(dev, "POWER_SUPPLY_NAME=%s\n", psy->name); -+ -+ ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, -+ &length, "POWER_SUPPLY_NAME=%s", psy->name); -+ if (ret) -+ return ret; -+ -+ prop_buf = (char *)get_zeroed_page(GFP_KERNEL); -+ if (!prop_buf) -+ return -ENOMEM; -+ -+ for (j = 0; j < ARRAY_SIZE(power_supply_static_attrs); j++) { -+ struct device_attribute *attr; -+ char *line; -+ -+ attr = &power_supply_static_attrs[j]; -+ -+ ret = power_supply_show_static_attrs(dev, attr, prop_buf); -+ if (ret < 0) -+ goto out; -+ -+ line = strchr(prop_buf, '\n'); -+ if (line) -+ *line = 0; -+ -+ attrname = kstruprdup(attr->attr.name, GFP_KERNEL); -+ if (!attrname) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ -+ dev_dbg(dev, "Static prop %s=%s\n", attrname, prop_buf); -+ -+ ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, -+ &length, "POWER_SUPPLY_%s=%s", -+ attrname, prop_buf); -+ kfree(attrname); -+ if (ret) -+ goto out; -+ } -+ -+ dev_dbg(dev, "%zd dynamic props\n", psy->num_properties); -+ -+ for (j = 0; j < psy->num_properties; j++) { -+ struct device_attribute *attr; -+ char *line; -+ -+ attr = &power_supply_attrs[psy->properties[j]]; -+ -+ ret = power_supply_show_property(dev, attr, prop_buf); -+ if (ret < 0) -+ goto out; -+ -+ line = strchr(prop_buf, '\n'); -+ if (line) -+ *line = 0; -+ -+ attrname = kstruprdup(attr->attr.name, GFP_KERNEL); -+ if (!attrname) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ -+ dev_dbg(dev, "prop %s=%s\n", attrname, prop_buf); -+ -+ ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, -+ &length, "POWER_SUPPLY_%s=%s", -+ attrname, prop_buf); -+ kfree(attrname); -+ if (ret) -+ goto out; -+ } -+ -+out: -+ free_page((unsigned long)prop_buf); -+ -+ return ret; -+} -Index: linux-2.6.22/drivers/power/simpad-battery.c -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/drivers/power/simpad-battery.c 2007-08-23 12:13:52.000000000 +0200 -@@ -0,0 +1,242 @@ -+/* -+ * linux/drivers/misc/simpad-battery.c -+ * -+ * Copyright (C) 2005 Holger Hans Peter Freyther -+ * Copyright (C) 2001 Juergen Messerer -+ * -+ * 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 -+ * the Free Software Foundation; either version 2 of the License. -+ * -+ * Read the Battery Level through the UCB1x00 chip. T-Sinuspad is -+ * unsupported for now. -+ * -+ */ -+ -+#include <linux/battery.h> -+#include <asm/dma.h> -+#include "ucb1x00.h" -+ -+ -+/* -+ * Conversion from AD -> mV -+ * 7.5V = 1023 7.3313mV/Digit -+ * -+ * 400 Units == 9.7V -+ * a = ADC value -+ * 21 = ADC error -+ * 12600 = Divident to get 2*7.3242 -+ * 860 = Divider to get 2*7.3242 -+ * 170 = Voltagedrop over -+ */ -+#define CALIBRATE_BATTERY(a) ((((a + 21)*12600)/860) + 170) -+ -+/* -+ * We have two types of batteries a small and a large one -+ * To get the right value we to distinguish between those two -+ * 450 Units == 15 V -+ */ -+#define CALIBRATE_SUPPLY(a) (((a) * 1500) / 45) -+#define MIN_SUPPLY 12000 /* Less then 12V means no powersupply */ -+ -+/* -+ * Charging Current -+ * if value is >= 50 then charging is on -+ */ -+#define CALIBRATE_CHARGING(a) (((a)* 1000)/(152/4))) -+ -+struct simpad_battery_t { -+ struct battery battery; -+ struct ucb1x00* ucb; -+ -+ /* -+ * Variables for the values to one time support -+ * T-Sinuspad as well -+ */ -+ int min_voltage; -+ int min_current; -+ int min_charge; -+ -+ int max_voltage; -+ int max_current; -+ int max_charge; -+ -+ int min_supply; -+ int charging_led_label; -+ int charging_max_label; -+ int batt_full; -+ int batt_low; -+ int batt_critical; -+ int batt_empty; -+}; -+ -+static int simpad_get_min_voltage(struct battery* _battery ) -+{ -+ struct simpad_battery_t* battery = (struct simpad_battery_t*)_battery; -+ return battery->min_voltage; -+} -+ -+static int simpad_get_min_current(struct battery* _battery) -+{ -+ struct simpad_battery_t* battery = (struct simpad_battery_t*)_battery; -+ return battery->min_current; -+} -+ -+static int simpad_get_min_charge(struct battery* _battery) -+{ -+ struct simpad_battery_t* battery = (struct simpad_battery_t*)_battery; -+ return battery->min_charge; -+} -+ -+static int simpad_get_max_voltage(struct battery* _battery) -+{ -+ struct simpad_battery_t* battery = (struct simpad_battery_t*)_battery; -+ return battery->max_voltage; -+} -+ -+static int simpad_get_max_current(struct battery* _battery) -+{ -+ struct simpad_battery_t* battery = (struct simpad_battery_t*)_battery; -+ return battery->max_current; -+} -+ -+static int simpad_get_max_charge(struct battery* _battery) -+{ -+ struct simpad_battery_t* battery = (struct simpad_battery_t*)_battery; -+ return battery->max_charge; -+} -+ -+static int simpad_get_temp(struct battery* _battery) -+{ -+ return 0; -+} -+ -+static int simpad_get_voltage(struct battery* _battery) -+{ -+ int val; -+ struct simpad_battery_t* battery = (struct simpad_battery_t*)_battery; -+ -+ -+ ucb1x00_adc_enable(battery->ucb); -+ val = ucb1x00_adc_read(battery->ucb, UCB_ADC_INP_AD1, UCB_NOSYNC); -+ ucb1x00_adc_disable(battery->ucb); -+ -+ return CALIBRATE_BATTERY(val); -+} -+ -+static int simpad_get_current(struct battery* _battery) -+{ -+ int val; -+ struct simpad_battery_t* battery = (struct simpad_battery_t*)_battery; -+ -+ ucb1x00_adc_enable(battery->ucb); -+ val = ucb1x00_adc_read(battery->ucb, UCB_ADC_INP_AD3, UCB_NOSYNC); -+ ucb1x00_adc_disable(battery->ucb); -+ -+ return val; -+} -+ -+static int simpad_get_charge(struct battery* _battery) -+{ -+ int val; -+ struct simpad_battery_t* battery = (struct simpad_battery_t*)_battery; -+ -+ ucb1x00_adc_enable(battery->ucb); -+ val = ucb1x00_adc_read(battery->ucb, UCB_ADC_INP_AD2, UCB_NOSYNC); -+ ucb1x00_adc_disable(battery->ucb); -+ -+ return CALIBRATE_SUPPLY(val); -+ -+} -+ -+static int simpad_get_status(struct battery* _battery) -+{ -+ struct simpad_battery_t* battery = (struct simpad_battery_t*)(_battery); -+ int vcharger = simpad_get_voltage(_battery); -+ int icharger = simpad_get_current(_battery); -+ -+ int status = BATTERY_STATUS_UNKNOWN; -+ if(icharger > battery->charging_led_label) -+ status = BATTERY_STATUS_CHARGING; -+ else if(vcharger > battery->min_supply) -+ status = BATTERY_STATUS_NOT_CHARGING; -+ else -+ status = BATTERY_STATUS_DISCHARGING; -+ -+ return status; -+} -+ -+static struct simpad_battery_t simpad_battery = { -+ .battery = { -+ .get_min_voltage = simpad_get_min_voltage, -+ .get_min_current = simpad_get_min_current, -+ .get_min_charge = simpad_get_min_charge, -+ .get_max_voltage = simpad_get_max_voltage, -+ .get_max_current = simpad_get_max_current, -+ .get_max_charge = simpad_get_max_charge, -+ .get_temp = simpad_get_temp, -+ .get_voltage = simpad_get_voltage, -+ .get_current = simpad_get_current, -+ .get_charge = simpad_get_charge, -+ .get_status = simpad_get_status, -+ }, -+ .min_voltage = 0, -+ .min_current = 0, -+ .min_charge = 0, -+ .max_voltage = 0, -+ .max_current = 0, -+ .max_charge = 0, -+ -+ .min_supply = 1200, -+ .charging_led_label = 18, -+ .charging_max_label = 265, -+ .batt_full = 8300, -+ .batt_low = 7300, -+ .batt_critical = 6800, -+ .batt_empty = 6500, -+}; -+ -+ -+ -+/* -+ * UCB glue code -+ */ -+static int ucb1x00_battery_add(struct class_device *dev) -+{ -+ struct ucb1x00 *ucb = classdev_to_ucb1x00(dev); -+ simpad_battery.ucb = ucb; -+ -+ battery_class_register(&simpad_battery.battery); -+ -+ return 0; -+} -+ -+static void ucb1x00_battery_remove(struct class_device *dev) -+{ -+ return battery_class_unregister(&simpad_battery.battery); -+} -+ -+ -+static struct ucb1x00_class_interface ucb1x00_battery_interface = { -+ .interface = { -+ .add = ucb1x00_battery_add, -+ .remove = ucb1x00_battery_remove, -+ }, -+}; -+ -+ -+static int __init battery_register(void) -+{ -+ return ucb1x00_register_interface(&ucb1x00_battery_interface); -+} -+ -+static void __exit battery_unregister(void) -+{ -+ ucb1x00_unregister_interface(&ucb1x00_battery_interface); -+} -+ -+module_init(battery_register); -+module_exit(battery_unregister); -+ -+MODULE_AUTHOR("Holger Hans Peter Freyther"); -+MODULE_LICENSE("GPL"); -Index: linux-2.6.22/arch/arm/Kconfig -=================================================================== ---- linux-2.6.22.orig/arch/arm/Kconfig 2007-08-23 12:17:42.000000000 +0200 -+++ linux-2.6.22/arch/arm/Kconfig 2007-08-23 12:22:28.000000000 +0200 -@@ -1016,6 +1016,8 @@ - - source "drivers/w1/Kconfig" - -+source "drivers/power/Kconfig" -+ - source "drivers/hwmon/Kconfig" - - #source "drivers/l3/Kconfig" -Index: linux-2.6.22/drivers/Kconfig -=================================================================== ---- linux-2.6.22.orig/drivers/Kconfig 2007-08-23 12:21:27.000000000 +0200 -+++ linux-2.6.22/drivers/Kconfig 2007-08-23 12:22:03.000000000 +0200 -@@ -54,6 +54,8 @@ - - source "drivers/w1/Kconfig" - -+source "drivers/power/Kconfig" -+ - source "drivers/hwmon/Kconfig" - - source "drivers/mfd/Kconfig" -Index: linux-2.6.22/drivers/Makefile -=================================================================== ---- linux-2.6.22.orig/drivers/Makefile 2007-08-23 12:33:58.000000000 +0200 -+++ linux-2.6.22/drivers/Makefile 2007-08-23 12:34:34.000000000 +0200 -@@ -61,6 +61,7 @@ - obj-$(CONFIG_RTC_LIB) += rtc/ - obj-y += i2c/ - obj-$(CONFIG_W1) += w1/ -+obj-$(CONFIG_POWER_SUPPLY) += power/ - obj-$(CONFIG_HWMON) += hwmon/ - obj-$(CONFIG_PHONE) += telephony/ - obj-$(CONFIG_MD) += md/ -Index: linux-2.6.22/include/linux/power_supply.h -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.22/include/linux/power_supply.h 2007-08-23 12:37:10.000000000 +0200 -@@ -0,0 +1,175 @@ -+/* -+ * Universal power supply monitor class -+ * -+ * Copyright (c) 2007 Anton Vorontsov <cbou@mail.ru> -+ * Copyright (c) 2004 Szabolcs Gyurko -+ * Copyright (c) 2003 Ian Molton <spyro@f2s.com> -+ * -+ * Modified: 2004, Oct Szabolcs Gyurko -+ * -+ * You may use this code as per GPL version 2 -+ */ -+ -+#ifndef __LINUX_POWER_SUPPLY_H__ -+#define __LINUX_POWER_SUPPLY_H__ -+ -+#include <linux/device.h> -+#include <linux/workqueue.h> -+#include <linux/leds.h> -+ -+/* -+ * All voltages, currents, charges, energies, time and temperatures in uV, -+ * uA, uAh, uWh, seconds and tenths of degree Celsius unless otherwise -+ * stated. It's driver's job to convert its raw values to units in which -+ * this class operates. -+ */ -+ -+/* -+ * For systems where the charger determines the maximum battery capacity -+ * the min and max fields should be used to present these values to user -+ * space. Unused/unknown fields will not appear in sysfs. -+ */ -+ -+enum { -+ POWER_SUPPLY_STATUS_UNKNOWN = 0, -+ POWER_SUPPLY_STATUS_CHARGING, -+ POWER_SUPPLY_STATUS_DISCHARGING, -+ POWER_SUPPLY_STATUS_NOT_CHARGING, -+ POWER_SUPPLY_STATUS_FULL, -+}; -+ -+enum { -+ POWER_SUPPLY_HEALTH_UNKNOWN = 0, -+ POWER_SUPPLY_HEALTH_GOOD, -+ POWER_SUPPLY_HEALTH_OVERHEAT, -+ POWER_SUPPLY_HEALTH_DEAD, -+}; -+ -+enum { -+ POWER_SUPPLY_TECHNOLOGY_UNKNOWN = 0, -+ POWER_SUPPLY_TECHNOLOGY_NIMH, -+ POWER_SUPPLY_TECHNOLOGY_LION, -+ POWER_SUPPLY_TECHNOLOGY_LIPO, -+}; -+ -+enum { -+ POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN = 0, -+ POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL, -+ POWER_SUPPLY_CAPACITY_LEVEL_LOW, -+ POWER_SUPPLY_CAPACITY_LEVEL_NORMAL, -+ POWER_SUPPLY_CAPACITY_LEVEL_HIGH, -+ POWER_SUPPLY_CAPACITY_LEVEL_FULL, -+}; -+ -+enum power_supply_property { -+ /* Properties of type `int' */ -+ POWER_SUPPLY_PROP_STATUS = 0, -+ POWER_SUPPLY_PROP_HEALTH, -+ POWER_SUPPLY_PROP_PRESENT, -+ POWER_SUPPLY_PROP_ONLINE, -+ POWER_SUPPLY_PROP_TECHNOLOGY, -+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, -+ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, -+ POWER_SUPPLY_PROP_VOLTAGE_NOW, -+ POWER_SUPPLY_PROP_VOLTAGE_AVG, -+ POWER_SUPPLY_PROP_CURRENT_NOW, -+ POWER_SUPPLY_PROP_CURRENT_AVG, -+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, -+ POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN, -+ POWER_SUPPLY_PROP_CHARGE_FULL, -+ POWER_SUPPLY_PROP_CHARGE_EMPTY, -+ POWER_SUPPLY_PROP_CHARGE_NOW, -+ POWER_SUPPLY_PROP_CHARGE_AVG, -+ POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, -+ POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN, -+ POWER_SUPPLY_PROP_ENERGY_FULL, -+ POWER_SUPPLY_PROP_ENERGY_EMPTY, -+ POWER_SUPPLY_PROP_ENERGY_NOW, -+ POWER_SUPPLY_PROP_ENERGY_AVG, -+ POWER_SUPPLY_PROP_CAPACITY, /* in percents! */ -+ POWER_SUPPLY_PROP_CAPACITY_LEVEL, -+ POWER_SUPPLY_PROP_TEMP, -+ POWER_SUPPLY_PROP_TEMP_AMBIENT, -+ POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, -+ POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, -+ POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, -+ POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, -+ /* Properties of type `const char *' */ -+ POWER_SUPPLY_PROP_MODEL_NAME, -+}; -+ -+enum power_supply_type { -+ POWER_SUPPLY_TYPE_BATTERY = 0, -+ POWER_SUPPLY_TYPE_UPS, -+ POWER_SUPPLY_TYPE_MAINS, -+ POWER_SUPPLY_TYPE_USB, -+}; -+ -+union power_supply_propval { -+ int intval; -+ const char *strval; -+}; -+ -+struct power_supply { -+ const char *name; -+ enum power_supply_type type; -+ enum power_supply_property *properties; -+ size_t num_properties; -+ -+ char **supplied_to; -+ size_t num_supplicants; -+ -+ int (*get_property)(struct power_supply *psy, -+ enum power_supply_property psp, -+ union power_supply_propval *val); -+ void (*external_power_changed)(struct power_supply *psy); -+ -+ /* For APM emulation, think legacy userspace. */ -+ int use_for_apm; -+ -+ /* private */ -+ struct device *dev; -+ struct work_struct changed_work; -+ -+#ifdef CONFIG_LEDS_TRIGGERS -+ struct led_trigger *charging_full_trig; -+ char *charging_full_trig_name; -+ struct led_trigger *charging_trig; -+ char *charging_trig_name; -+ struct led_trigger *full_trig; -+ char *full_trig_name; -+ struct led_trigger *online_trig; -+ char *online_trig_name; -+#endif -+}; -+ -+/* -+ * This is recommended structure to specify static power supply parameters. -+ * Generic one, parametrizable for different power supplies. Power supply -+ * class itself does not use it, but that's what implementing most platform -+ * drivers, should try reuse for consistency. -+ */ -+ -+struct power_supply_info { -+ const char *name; -+ int technology; -+ int voltage_max_design; -+ int voltage_min_design; -+ int charge_full_design; -+ int charge_empty_design; -+ int energy_full_design; -+ int energy_empty_design; -+ int use_for_apm; -+}; -+ -+extern void power_supply_changed(struct power_supply *psy); -+extern int power_supply_am_i_supplied(struct power_supply *psy); -+ -+extern int power_supply_register(struct device *parent, -+ struct power_supply *psy); -+extern void power_supply_unregister(struct power_supply *psy); -+ -+/* For APM emulation, think legacy userspace. */ -+extern struct class *power_supply_class; -+ -+#endif /* __LINUX_POWER_SUPPLY_H__ */ diff --git a/meta/packages/linux/linux-rp-2.6.23/versatile-armv6.patch b/meta/packages/linux/linux-rp-2.6.23/versatile-armv6.patch new file mode 100644 index 0000000000..e2d0060ac3 --- /dev/null +++ b/meta/packages/linux/linux-rp-2.6.23/versatile-armv6.patch @@ -0,0 +1,19 @@ +--- + arch/arm/mm/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- linux-2.6.23.orig/arch/arm/mm/Kconfig ++++ linux-2.6.23/arch/arm/mm/Kconfig +@@ -343,11 +343,11 @@ config CPU_XSC3 + select IO_36 + + # ARMv6 + config CPU_V6 + bool "Support ARM V6 processor" +- depends on ARCH_INTEGRATOR || MACH_REALVIEW_EB || ARCH_OMAP2 || ARCH_MX3 ++ depends on ARCH_INTEGRATOR || MACH_REALVIEW_EB || ARCH_OMAP2 || ARCH_MX3 || ARCH_VERSATILE_PB + default y if ARCH_MX3 + select CPU_32v6 + select CPU_ABRT_EV6 + select CPU_CACHE_V6 + select CPU_CACHE_VIPT diff --git a/meta/packages/linux/linux-rp-2.6.23/vt_ioctl_race.patch b/meta/packages/linux/linux-rp-2.6.23/vt_ioctl_race.patch deleted file mode 100644 index 5a51d1c3f5..0000000000 --- a/meta/packages/linux/linux-rp-2.6.23/vt_ioctl_race.patch +++ /dev/null @@ -1,46 +0,0 @@ ---- - drivers/char/vt_ioctl.c | 8 +++++--- - 1 file changed, 5 insertions(+), 3 deletions(-) - -Index: linux-2.6.22/drivers/char/vt_ioctl.c -=================================================================== ---- linux-2.6.22.orig/drivers/char/vt_ioctl.c 2007-07-09 01:32:17.000000000 +0200 -+++ linux-2.6.22/drivers/char/vt_ioctl.c 2007-09-27 11:58:42.000000000 +0200 -@@ -770,6 +770,7 @@ - /* - * Switching-from response - */ -+ acquire_console_sem(); - if (vc->vt_newvt >= 0) { - if (arg == 0) - /* -@@ -784,7 +785,6 @@ - * complete the switch. - */ - int newvt; -- acquire_console_sem(); - newvt = vc->vt_newvt; - vc->vt_newvt = -1; - i = vc_allocate(newvt); -@@ -798,7 +798,6 @@ - * other console switches.. - */ - complete_change_console(vc_cons[newvt].d); -- release_console_sem(); - } - } - -@@ -810,9 +809,12 @@ - /* - * If it's just an ACK, ignore it - */ -- if (arg != VT_ACKACQ) -+ if (arg != VT_ACKACQ) { -+ release_console_sem(); - return -EINVAL; -+ } - } -+ release_console_sem(); - - return 0; - diff --git a/meta/packages/linux/linux-rp-2.6.23/w100fb-unused-var.patch b/meta/packages/linux/linux-rp-2.6.23/w100fb-unused-var.patch deleted file mode 100644 index 8cbbb6bd01..0000000000 --- a/meta/packages/linux/linux-rp-2.6.23/w100fb-unused-var.patch +++ /dev/null @@ -1,17 +0,0 @@ -From: Marcin Juszkiewicz <openembedded@haerwu.biz> - -drivers/video/w100fb.c: In function ‘w100fb_imageblit’: -drivers/video/w100fb.c:507: warning: unused variable ‘par’ - -Signed-off-by: Marcin Juszkiewicz <openembedded@haerwu.biz> - ---- linux-2.6.23/drivers/video/w100fb.c 2007-10-11 16:52:30.000000000 +0200 -+++ linux-2.6.23/drivers/video/w100fb.c 2007-10-15 12:56:01.000000000 +0200 -@@ -504,7 +504,6 @@ static void w100_hostdata(u32 width, u32 - static void w100fb_imageblit(struct fb_info *info, - const struct fb_image *image) - { -- struct w100fb_par *par = info->par; - union dp_gui_master_cntl_u gmc; - u32 fgcolor, bgcolor; - diff --git a/meta/packages/linux/linux-rp-2.6.23/wm9712-reset-loop-r2.patch b/meta/packages/linux/linux-rp-2.6.23/wm9712-reset-loop-r2.patch new file mode 100644 index 0000000000..78e81ea83a --- /dev/null +++ b/meta/packages/linux/linux-rp-2.6.23/wm9712-reset-loop-r2.patch @@ -0,0 +1,44 @@ + sound/soc/codecs/wm9712.c | 28 ++++++++++++++++++---------- + 1 file changed, 18 insertions(+), 10 deletions(-) + +Index: git/sound/soc/codecs/wm9712.c +=================================================================== +--- git.orig/sound/soc/codecs/wm9712.c 2006-11-07 22:10:01.000000000 +0000 ++++ git/sound/soc/codecs/wm9712.c 2006-11-07 22:11:50.000000000 +0000 +@@ -618,18 +618,26 @@ static int wm9712_dapm_event(struct snd_ + + static int wm9712_reset(struct snd_soc_codec *codec, int try_warm) + { +- if (try_warm && soc_ac97_ops.warm_reset) { +- soc_ac97_ops.warm_reset(codec->ac97); +- if (!(ac97_read(codec, 0) & 0x8000)) +- return 1; +- } ++ int retry = 3; + +- soc_ac97_ops.reset(codec->ac97); +- if (ac97_read(codec, 0) & 0x8000) +- goto err; +- return 0; ++ while (retry--) ++ { ++ if(try_warm && soc_ac97_ops.warm_reset) { ++ soc_ac97_ops.warm_reset(codec->ac97); ++ if(ac97_read(codec, 0) & 0x8000) ++ continue; ++ else ++ return 1; ++ } ++ ++ soc_ac97_ops.reset(codec->ac97); ++ if(ac97_read(codec, 0) & 0x8000) ++ continue; ++ else ++ return 0; ++ ++ } + +-err: + printk(KERN_ERR "WM9712 AC97 reset failed\n"); + return -EIO; + } diff --git a/meta/packages/linux/linux-rp-2.6.23/wm9712-suspend-cold-res-r2.patch b/meta/packages/linux/linux-rp-2.6.23/wm9712-suspend-cold-res-r2.patch new file mode 100644 index 0000000000..5179b47cc4 --- /dev/null +++ b/meta/packages/linux/linux-rp-2.6.23/wm9712-suspend-cold-res-r2.patch @@ -0,0 +1,16 @@ + sound/soc/codecs/wm9712.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +Index: git/sound/soc/codecs/wm9712.c +=================================================================== +--- git.orig/sound/soc/codecs/wm9712.c 2006-11-07 21:57:34.000000000 +0000 ++++ git/sound/soc/codecs/wm9712.c 2006-11-07 21:59:30.000000000 +0000 +@@ -651,7 +651,7 @@ static int wm9712_soc_resume(struct plat + int i, ret; + u16 *cache = codec->reg_cache; + +- ret = wm9712_reset(codec, 1); ++ ret = wm9712_reset(codec, 0); + if (ret < 0){ + printk(KERN_ERR "could not reset AC97 codec\n"); + return ret; diff --git a/meta/packages/linux/linux-rp-2.6.23/wm97xx-lg13-r0-fix-r0.patch b/meta/packages/linux/linux-rp-2.6.23/wm97xx-lg13-r0-fix-r0.patch new file mode 100644 index 0000000000..5ad0d8703d --- /dev/null +++ b/meta/packages/linux/linux-rp-2.6.23/wm97xx-lg13-r0-fix-r0.patch @@ -0,0 +1,128 @@ + drivers/input/power.c | 2 +- + drivers/input/touchscreen/Kconfig | 2 +- + drivers/input/touchscreen/wm97xx-core.c | 35 ++++++++++++++++--------------- + include/linux/wm97xx.h | 2 +- + 4 files changed, 21 insertions(+), 20 deletions(-) + +diff --git a/drivers/input/power.c b/drivers/input/power.c +index 4443e34..7aac875 100644 +--- a/drivers/input/power.c ++++ b/drivers/input/power.c +@@ -156,7 +156,7 @@ static void power_event(struct input_handle *handle, unsigned int type, + } + } + +-static struct input_handle *power_connect(struct input_handler *handler, ++static int power_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id) + { +diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig +index 6862e8f..9b532e9 100644 +--- a/drivers/input/touchscreen/Kconfig ++++ b/drivers/input/touchscreen/Kconfig +@@ -247,7 +247,7 @@ config TOUCHSCREEN_TSC2101 + + config TOUCHSCREEN_WM97XX + tristate "Support for WM97xx AC97 touchscreen controllers" +- depends SND_AC97_BUS ++ depends AC97_BUS + + choice + prompt "WM97xx codec type" +diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c +index 9b2710e..d3ce3f3 100644 +--- a/drivers/input/touchscreen/wm97xx-core.c ++++ b/drivers/input/touchscreen/wm97xx-core.c +@@ -84,6 +84,7 @@ + #include <linux/bitops.h> + #include <linux/workqueue.h> + #include <linux/device.h> ++#include <linux/freezer.h> + #include <linux/wm97xx.h> + #include <asm/uaccess.h> + #include <asm/io.h> +@@ -241,14 +242,15 @@ WM97XX_STATUS_ATTR(gpio); + + static int wm97xx_sys_add(struct device *dev) + { ++ int err; + if (aux_sys) { +- device_create_file(dev, &dev_attr_aux1); +- device_create_file(dev, &dev_attr_aux2); +- device_create_file(dev, &dev_attr_aux3); +- device_create_file(dev, &dev_attr_aux4); ++ err = device_create_file(dev, &dev_attr_aux1); ++ err = device_create_file(dev, &dev_attr_aux2); ++ err = device_create_file(dev, &dev_attr_aux3); ++ err = device_create_file(dev, &dev_attr_aux4); + } + if (status_sys) +- device_create_file(dev, &dev_attr_gpio); ++ err = device_create_file(dev, &dev_attr_gpio); + return 0; + } + +@@ -366,12 +368,12 @@ void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, wm97xx_gpio_dir_t dir, + + /* + * Handle a pen down interrupt. +- */ +-static void wm97xx_pen_irq_worker(void *ptr) +-{ +- struct wm97xx *wm = (struct wm97xx *) ptr; +- +- /* do we need to enable the touch panel reader */ ++ */ ++static void wm97xx_pen_irq_worker(struct work_struct *work) ++{ ++ struct wm97xx *wm = container_of(work, struct wm97xx, pen_event_work); ++ ++ /* do we need to enable the touch panel reader */ + if (wm->id == WM9705_ID2) { + if (wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD) & WM97XX_PEN_DOWN) + wm->pen_is_down = 1; +@@ -411,9 +413,8 @@ static void wm97xx_pen_irq_worker(void *ptr) + * We have to disable the codec interrupt in the handler because it can + * take upto 1ms to clear the interrupt source. The interrupt is then enabled + * again in the slow handler when the source has been cleared. +- */ +-static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id, +- struct pt_regs *regs) ++ */ ++static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id) + { + struct wm97xx *wm = (struct wm97xx *) dev_id; + disable_irq(wm->pen_irq); +@@ -428,15 +429,15 @@ static int wm97xx_init_pen_irq(struct wm97xx *wm) + { + u16 reg; + +- INIT_WORK(&wm->pen_event_work, wm97xx_pen_irq_worker, wm); +- if ((wm->pen_irq_workq = ++ INIT_WORK(&wm->pen_event_work, wm97xx_pen_irq_worker); ++ if ((wm->pen_irq_workq = + create_singlethread_workqueue("kwm97pen")) == NULL) { + err("could not create pen irq work queue"); + wm->pen_irq = 0; + return -EINVAL; + } + +- if (request_irq (wm->pen_irq, wm97xx_pen_interrupt, SA_SHIRQ, "wm97xx-pen", wm)) { ++ if (request_irq (wm->pen_irq, wm97xx_pen_interrupt, IRQF_SHARED, "wm97xx-pen", wm)) { + err("could not register codec pen down interrupt, will poll for pen down"); + destroy_workqueue(wm->pen_irq_workq); + wm->pen_irq = 0; +diff --git a/include/linux/wm97xx.h b/include/linux/wm97xx.h +index b1c1740..a9bd57e 100644 +--- a/include/linux/wm97xx.h ++++ b/include/linux/wm97xx.h +@@ -243,7 +243,7 @@ struct wm97xx { + u16 dig_save[3]; /* saved during aux reading */ + struct wm97xx_codec_drv *codec; /* attached codec driver*/ + struct input_dev* input_dev; /* touchscreen input device */ +- ac97_t *ac97; /* ALSA codec access */ ++ struct snd_ac97 *ac97; /* ALSA codec access */ + struct device *dev; /* ALSA device */ + struct device *battery_dev; + struct device *touch_dev; diff --git a/meta/packages/linux/linux-rp-2.6.23/wm97xx-lg13-r0.patch b/meta/packages/linux/linux-rp-2.6.23/wm97xx-lg13-r0.patch new file mode 100644 index 0000000000..c918c5daff --- /dev/null +++ b/meta/packages/linux/linux-rp-2.6.23/wm97xx-lg13-r0.patch @@ -0,0 +1,2899 @@ +Index: linux-2.6.17/drivers/input/touchscreen/Kconfig +=================================================================== +--- linux-2.6.17.orig/drivers/input/touchscreen/Kconfig 2006-09-19 20:35:35.060495500 +0200 ++++ linux-2.6.17/drivers/input/touchscreen/Kconfig 2006-09-19 20:36:47.965051750 +0200 +@@ -121,4 +121,57 @@ config TOUCHSCREEN_TSC2101 + To compile this driver as a module, choose M here: the + module will be called ads7846_ts. + ++config TOUCHSCREEN_WM97XX ++ tristate "Support for WM97xx AC97 touchscreen controllers" ++ depends SND_AC97_BUS ++ ++choice ++ prompt "WM97xx codec type" ++ ++config TOUCHSCREEN_WM9705 ++ bool "WM9705 Touchscreen interface support" ++ depends on TOUCHSCREEN_WM97XX ++ help ++ Say Y here if you have the wm9705 touchscreen. ++ ++ If unsure, say N. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called wm9705. ++ ++config TOUCHSCREEN_WM9712 ++ bool "WM9712 Touchscreen interface support" ++ depends on TOUCHSCREEN_WM97XX ++ help ++ Say Y here if you have the wm9712 touchscreen. ++ ++ If unsure, say N. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called wm9712. ++ ++config TOUCHSCREEN_WM9713 ++ bool "WM9713 Touchscreen interface support" ++ depends on TOUCHSCREEN_WM97XX ++ help ++ Say Y here if you have the wm9713 touchscreen. ++ ++ If unsure, say N. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called wm9713. ++ ++endchoice ++ ++config TOUCHSCREEN_WM97XX_PXA ++ tristate "WM97xx PXA accelerated touch" ++ depends on TOUCHSCREEN_WM97XX && ARCH_PXA ++ help ++ Say Y here for continuous mode touch on the PXA ++ ++ If unsure, say N ++ ++ To compile this driver as a module, choose M here: the ++ module will be called pxa-wm97xx ++ + endif +Index: linux-2.6.17/drivers/input/touchscreen/Makefile +=================================================================== +--- linux-2.6.17.orig/drivers/input/touchscreen/Makefile 2006-09-19 20:35:35.072496250 +0200 ++++ linux-2.6.17/drivers/input/touchscreen/Makefile 2006-09-19 20:37:40.540337500 +0200 +@@ -4,6 +4,8 @@ + + # Each configuration option enables a list of files. + ++wm97xx-ts-objs := wm97xx-core.o ++ + obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o + obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o + obj-$(CONFIG_TOUCHSCREEN_CORGI) += corgi_ts.o +@@ -13,3 +15,16 @@ obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtou + obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o + obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o + obj-$(CONFIG_TOUCHSCREEN_TSC2101) += tsc2101_ts.o ++obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o ++obj-$(CONFIG_TOUCHSCREEN_WM97XX_PXA) += pxa-wm97xx.o ++ ++ifeq ($(CONFIG_TOUCHSCREEN_WM9713),y) ++wm97xx-ts-objs += wm9713.o ++endif ++ ++ifeq ($(CONFIG_TOUCHSCREEN_WM9712),y) ++wm97xx-ts-objs += wm9712.o ++endif ++ifeq ($(CONFIG_TOUCHSCREEN_WM9705),y) ++wm97xx-ts-objs += wm9705.o ++endif +Index: linux-2.6.17/drivers/input/touchscreen/pxa-wm97xx.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.17/drivers/input/touchscreen/pxa-wm97xx.c 2006-09-19 20:36:47.965051750 +0200 +@@ -0,0 +1,289 @@ ++/* ++ * pxa-wm97xx.c -- pxa-wm97xx Continuous Touch screen driver for ++ * Wolfson WM97xx AC97 Codecs. ++ * ++ * Copyright 2004 Wolfson Microelectronics PLC. ++ * Author: Liam Girdwood ++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com ++ * Parts Copyright : Ian Molton <spyro@f2s.com> ++ * Andrew Zabolotny <zap@homelink.ru> ++ * ++ * 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 the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * Notes: ++ * This is a wm97xx extended touch driver to capture touch ++ * data in a continuous manner on the Intel XScale archictecture ++ * ++ * Features: ++ * - codecs supported:- WM9705, WM9712, WM9713 ++ * - processors supported:- Intel XScale PXA25x, PXA26x, PXA27x ++ * ++ * Revision history ++ * 18th Aug 2004 Initial version. ++ * 26th Jul 2005 Improved continous read back and added FIFO flushing. ++ * 06th Sep 2005 Mike Arthur <linux@wolfsonmicro.com> ++ * Moved to using the wm97xx bus ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/version.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/delay.h> ++#include <linux/irq.h> ++#include <linux/wm97xx.h> ++#include <asm/io.h> ++#include <asm/arch/pxa-regs.h> ++ ++#define VERSION "0.13" ++ ++struct continuous { ++ u16 id; /* codec id */ ++ u8 code; /* continuous code */ ++ u8 reads; /* number of coord reads per read cycle */ ++ u32 speed; /* number of coords per second */ ++}; ++ ++#define WM_READS(sp) ((sp / HZ) + 1) ++ ++static const struct continuous cinfo[] = { ++ {WM9705_ID2, 0, WM_READS(94), 94}, ++ {WM9705_ID2, 1, WM_READS(188), 188}, ++ {WM9705_ID2, 2, WM_READS(375), 375}, ++ {WM9705_ID2, 3, WM_READS(750), 750}, ++ {WM9712_ID2, 0, WM_READS(94), 94}, ++ {WM9712_ID2, 1, WM_READS(188), 188}, ++ {WM9712_ID2, 2, WM_READS(375), 375}, ++ {WM9712_ID2, 3, WM_READS(750), 750}, ++ {WM9713_ID2, 0, WM_READS(94), 94}, ++ {WM9713_ID2, 1, WM_READS(120), 120}, ++ {WM9713_ID2, 2, WM_READS(154), 154}, ++ {WM9713_ID2, 3, WM_READS(188), 188}, ++}; ++ ++/* continuous speed index */ ++static int sp_idx = 0; ++static u16 last = 0, tries = 0; ++ ++/* ++ * Pen sampling frequency (Hz) in continuous mode. ++ */ ++static int cont_rate = 200; ++module_param(cont_rate, int, 0); ++MODULE_PARM_DESC(cont_rate, "Sampling rate in continuous mode (Hz)"); ++ ++/* ++ * Pen down detection. ++ * ++ * This driver can either poll or use an interrupt to indicate a pen down ++ * event. If the irq request fails then it will fall back to polling mode. ++ */ ++static int pen_int = 1; ++module_param(pen_int, int, 0); ++MODULE_PARM_DESC(pen_int, "Pen down detection (1 = interrupt, 0 = polling)"); ++ ++/* ++ * Pressure readback. ++ * ++ * Set to 1 to read back pen down pressure ++ */ ++static int pressure = 0; ++module_param(pressure, int, 0); ++MODULE_PARM_DESC(pressure, "Pressure readback (1 = pressure, 0 = no pressure)"); ++ ++/* ++ * AC97 touch data slot. ++ * ++ * Touch screen readback data ac97 slot ++ */ ++static int ac97_touch_slot = 5; ++module_param(ac97_touch_slot, int, 0); ++MODULE_PARM_DESC(ac97_touch_slot, "Touch screen data slot AC97 number"); ++ ++ ++/* flush AC97 slot 5 FIFO on pxa machines */ ++#ifdef CONFIG_PXA27x ++void wm97xx_acc_pen_up (struct wm97xx* wm) ++{ ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule_timeout(1); ++ ++ while (MISR & (1 << 2)) ++ MODR; ++} ++#else ++void wm97xx_acc_pen_up (struct wm97xx* wm) ++{ ++ int count = 16; ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule_timeout(1); ++ ++ while (count < 16) { ++ MODR; ++ count--; ++ } ++} ++#endif ++ ++int wm97xx_acc_pen_down (struct wm97xx* wm) ++{ ++ u16 x, y, p = 0x100 | WM97XX_ADCSEL_PRES; ++ int reads = 0; ++ ++ /* data is never immediately available after pen down irq */ ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule_timeout(1); ++ ++ if (tries > 5){ ++ tries = 0; ++ return RC_PENUP; ++ } ++ ++ x = MODR; ++ if (x == last) { ++ tries++; ++ return RC_AGAIN; ++ } ++ last = x; ++ do { ++ if (reads) ++ x= MODR; ++ y= MODR; ++ if (pressure) ++ p = MODR; ++ ++ /* are samples valid */ ++ if ((x & 0x7000) != WM97XX_ADCSEL_X || ++ (y & 0x7000) != WM97XX_ADCSEL_Y || ++ (p & 0x7000) != WM97XX_ADCSEL_PRES) ++ goto up; ++ ++ /* coordinate is good */ ++ tries = 0; ++ //printk("x %x y %x p %x\n", x,y,p); ++ input_report_abs (wm->input_dev, ABS_X, x & 0xfff); ++ input_report_abs (wm->input_dev, ABS_Y, y & 0xfff); ++ input_report_abs (wm->input_dev, ABS_PRESSURE, p & 0xfff); ++ input_sync (wm->input_dev); ++ reads++; ++ } while (reads < cinfo[sp_idx].reads); ++up: ++ return RC_PENDOWN | RC_AGAIN; ++} ++ ++int wm97xx_acc_startup(struct wm97xx* wm) ++{ ++ int idx = 0; ++ ++ /* check we have a codec */ ++ if (wm->ac97 == NULL) ++ return -ENODEV; ++ ++ /* Go you big red fire engine */ ++ for (idx = 0; idx < ARRAY_SIZE(cinfo); idx++) { ++ if (wm->id != cinfo[idx].id) ++ continue; ++ sp_idx = idx; ++ if (cont_rate <= cinfo[idx].speed) ++ break; ++ } ++ wm->acc_rate = cinfo[sp_idx].code; ++ wm->acc_slot = ac97_touch_slot; ++ printk(KERN_INFO "pxa2xx accelerated touchscreen driver, %d samples (sec)\n", ++ cinfo[sp_idx].speed); ++ ++ /* codec specific irq config */ ++ if (pen_int) { ++ switch (wm->id) { ++ case WM9705_ID2: ++ wm->pen_irq = IRQ_GPIO(4); ++ set_irq_type(IRQ_GPIO(4), IRQT_BOTHEDGE); ++ break; ++ case WM9712_ID2: ++ case WM9713_ID2: ++ /* enable pen down interrupt */ ++ /* use PEN_DOWN GPIO 13 to assert IRQ on GPIO line 2 */ ++ wm->pen_irq = MAINSTONE_AC97_IRQ; ++ wm97xx_config_gpio(wm, WM97XX_GPIO_13, WM97XX_GPIO_IN, ++ WM97XX_GPIO_POL_HIGH, WM97XX_GPIO_STICKY, WM97XX_GPIO_WAKE); ++ wm97xx_config_gpio(wm, WM97XX_GPIO_2, WM97XX_GPIO_OUT, ++ WM97XX_GPIO_POL_HIGH, WM97XX_GPIO_NOTSTICKY, WM97XX_GPIO_NOWAKE); ++ break; ++ default: ++ printk(KERN_WARNING "pen down irq not supported on this device\n"); ++ pen_int = 0; ++ break; ++ } ++ } ++ ++ return 0; ++} ++ ++void wm97xx_acc_shutdown(struct wm97xx* wm) ++{ ++ /* codec specific deconfig */ ++ if (pen_int) { ++ switch (wm->id & 0xffff) { ++ case WM9705_ID2: ++ wm->pen_irq = 0; ++ break; ++ case WM9712_ID2: ++ case WM9713_ID2: ++ /* disable interrupt */ ++ wm->pen_irq = 0; ++ break; ++ } ++ } ++} ++ ++static struct wm97xx_mach_ops pxa_mach_ops = { ++ .acc_enabled = 1, ++ .acc_pen_up = wm97xx_acc_pen_up, ++ .acc_pen_down = wm97xx_acc_pen_down, ++ .acc_startup = wm97xx_acc_startup, ++ .acc_shutdown = wm97xx_acc_shutdown, ++}; ++ ++int pxa_wm97xx_probe(struct device *dev) ++{ ++ struct wm97xx *wm = dev->driver_data; ++ return wm97xx_register_mach_ops (wm, &pxa_mach_ops); ++} ++ ++int pxa_wm97xx_remove(struct device *dev) ++{ ++ struct wm97xx *wm = dev->driver_data; ++ wm97xx_unregister_mach_ops (wm); ++ return 0; ++} ++ ++static struct device_driver pxa_wm97xx_driver = { ++ .name = "wm97xx-touchscreen", ++ .bus = &wm97xx_bus_type, ++ .owner = THIS_MODULE, ++ .probe = pxa_wm97xx_probe, ++ .remove = pxa_wm97xx_remove ++}; ++ ++static int __init pxa_wm97xx_init(void) ++{ ++ return driver_register(&pxa_wm97xx_driver); ++} ++ ++static void __exit pxa_wm97xx_exit(void) ++{ ++ driver_unregister(&pxa_wm97xx_driver); ++} ++ ++module_init(pxa_wm97xx_init); ++module_exit(pxa_wm97xx_exit); ++ ++/* Module information */ ++MODULE_AUTHOR("Liam Girdwood <liam.girdwood@wolfsonmicro.com>"); ++MODULE_DESCRIPTION("wm97xx continuous touch driver for pxa2xx"); ++MODULE_LICENSE("GPL"); +Index: linux-2.6.17/drivers/input/touchscreen/wm9705.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.17/drivers/input/touchscreen/wm9705.c 2006-09-19 20:36:47.969052000 +0200 +@@ -0,0 +1,360 @@ ++/* ++ * wm9705.c -- Codec driver for Wolfson WM9705 AC97 Codec. ++ * ++ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC. ++ * Author: Liam Girdwood ++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com ++ * Parts Copyright : Ian Molton <spyro@f2s.com> ++ * Andrew Zabolotny <zap@homelink.ru> ++ * Russell King <rmk@arm.linux.org.uk> ++ * ++ * 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 the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * Revision history ++ * 6th Sep 2006 Mike Arthur <linux@wolfsonmicro.com> ++ * Added pre and post sample calls. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/version.h> ++#include <linux/kernel.h> ++#include <linux/input.h> ++#include <linux/delay.h> ++#include <linux/bitops.h> ++#include <linux/wm97xx.h> ++ ++#define TS_NAME "wm97xx" ++#define WM9705_VERSION "0.62" ++#define DEFAULT_PRESSURE 0xb0c0 ++ ++/* ++ * Debug ++ */ ++#if 0 ++#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg) ++#else ++#define dbg(format, arg...) ++#endif ++#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg) ++#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg) ++#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg) ++ ++/* ++ * Module parameters ++ */ ++ ++/* ++ * Set current used for pressure measurement. ++ * ++ * Set pil = 2 to use 400uA ++ * pil = 1 to use 200uA and ++ * pil = 0 to disable pressure measurement. ++ * ++ * This is used to increase the range of values returned by the adc ++ * when measureing touchpanel pressure. ++ */ ++static int pil = 0; ++module_param(pil, int, 0); ++MODULE_PARM_DESC(pil, "Set current used for pressure measurement."); ++ ++/* ++ * Set threshold for pressure measurement. ++ * ++ * Pen down pressure below threshold is ignored. ++ */ ++static int pressure = DEFAULT_PRESSURE & 0xfff; ++module_param(pressure, int, 0); ++MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement."); ++ ++/* ++ * Set adc sample delay. ++ * ++ * For accurate touchpanel measurements, some settling time may be ++ * required between the switch matrix applying a voltage across the ++ * touchpanel plate and the ADC sampling the signal. ++ * ++ * This delay can be set by setting delay = n, where n is the array ++ * position of the delay in the array delay_table below. ++ * Long delays > 1ms are supported for completeness, but are not ++ * recommended. ++ */ ++static int delay = 4; ++module_param(delay, int, 0); ++MODULE_PARM_DESC(delay, "Set adc sample delay."); ++ ++/* ++ * Pen detect comparator threshold. ++ * ++ * 0 to Vmid in 15 steps, 0 = use zero power comparator with Vmid threshold ++ * i.e. 1 = Vmid/15 threshold ++ * 15 = Vmid/1 threshold ++ * ++ * Adjust this value if you are having problems with pen detect not ++ * detecting any down events. ++ */ ++static int pdd = 8; ++module_param(pdd, int, 0); ++MODULE_PARM_DESC(pdd, "Set pen detect comparator threshold"); ++ ++/* ++ * Set adc mask function. ++ * ++ * Sources of glitch noise, such as signals driving an LCD display, may feed ++ * through to the touch screen plates and affect measurement accuracy. In ++ * order to minimise this, a signal may be applied to the MASK pin to delay or ++ * synchronise the sampling. ++ * ++ * 0 = No delay or sync ++ * 1 = High on pin stops conversions ++ * 2 = Edge triggered, edge on pin delays conversion by delay param (above) ++ * 3 = Edge triggered, edge on pin starts conversion after delay param ++ */ ++static int mask = 0; ++module_param(mask, int, 0); ++MODULE_PARM_DESC(mask, "Set adc mask function."); ++ ++/* ++ * ADC sample delay times in uS ++ */ ++static const int delay_table[] = { ++ 21, // 1 AC97 Link frames ++ 42, // 2 ++ 84, // 4 ++ 167, // 8 ++ 333, // 16 ++ 667, // 32 ++ 1000, // 48 ++ 1333, // 64 ++ 2000, // 96 ++ 2667, // 128 ++ 3333, // 160 ++ 4000, // 192 ++ 4667, // 224 ++ 5333, // 256 ++ 6000, // 288 ++ 0 // No delay, switch matrix always on ++}; ++ ++/* ++ * Delay after issuing a POLL command. ++ * ++ * The delay is 3 AC97 link frames + the touchpanel settling delay ++ */ ++static inline void poll_delay(int d) ++{ ++ udelay (3 * AC97_LINK_FRAME + delay_table [d]); ++} ++ ++/* ++ * set up the physical settings of the WM9705 ++ */ ++static void init_wm9705_phy(struct wm97xx* wm) ++{ ++ u16 dig1 = 0, dig2 = WM97XX_RPR; ++ ++ /* ++ * mute VIDEO and AUX as they share X and Y touchscreen ++ * inputs on the WM9705 ++ */ ++ wm97xx_reg_write(wm, AC97_AUX, 0x8000); ++ wm97xx_reg_write(wm, AC97_VIDEO, 0x8000); ++ ++ /* touchpanel pressure current*/ ++ if (pil == 2) { ++ dig2 |= WM9705_PIL; ++ dbg("setting pressure measurement current to 400uA."); ++ } else if (pil) ++ dbg("setting pressure measurement current to 200uA."); ++ if(!pil) ++ pressure = 0; ++ ++ /* polling mode sample settling delay */ ++ if (delay!=4) { ++ if (delay < 0 || delay > 15) { ++ dbg("supplied delay out of range."); ++ delay = 4; ++ } ++ } ++ dig1 &= 0xff0f; ++ dig1 |= WM97XX_DELAY(delay); ++ dbg("setting adc sample delay to %d u Secs.", delay_table[delay]); ++ ++ /* WM9705 pdd */ ++ dig2 |= (pdd & 0x000f); ++ dbg("setting pdd to Vmid/%d", 1 - (pdd & 0x000f)); ++ ++ /* mask */ ++ dig2 |= ((mask & 0x3) << 4); ++ ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2); ++} ++ ++static int wm9705_digitiser_ioctl(struct wm97xx* wm, int cmd) ++{ ++ switch(cmd) { ++ case WM97XX_DIG_START: ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig[2] | WM97XX_PRP_DET_DIG); ++ wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */ ++ break; ++ case WM97XX_DIG_STOP: ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig[2] & ~WM97XX_PRP_DET_DIG); ++ break; ++ case WM97XX_AUX_PREPARE: ++ memcpy(wm->dig_save, wm->dig, sizeof(wm->dig)); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 0); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG); ++ break; ++ case WM97XX_DIG_RESTORE: ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]); ++ break; ++ case WM97XX_PHY_INIT: ++ init_wm9705_phy(wm); ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static inline int is_pden (struct wm97xx* wm) ++{ ++ return wm->dig[2] & WM9705_PDEN; ++} ++ ++/* ++ * Read a sample from the WM9705 adc in polling mode. ++ */ ++static int wm9705_poll_sample (struct wm97xx* wm, int adcsel, int *sample) ++{ ++ int timeout = 5 * delay; ++ ++ if (!wm->pen_probably_down) { ++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (!(data & WM97XX_PEN_DOWN)) ++ return RC_PENUP; ++ wm->pen_probably_down = 1; ++ } ++ ++ /* set up digitiser */ ++ if (adcsel & 0x8000) ++ adcsel = ((adcsel & 0x7fff) + 3) << 12; ++ ++ if (wm->mach_ops && wm->mach_ops->pre_sample) ++ wm->mach_ops->pre_sample(adcsel); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, adcsel | WM97XX_POLL | WM97XX_DELAY(delay)); ++ ++ /* wait 3 AC97 time slots + delay for conversion */ ++ poll_delay (delay); ++ ++ /* wait for POLL to go low */ ++ while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) { ++ udelay(AC97_LINK_FRAME); ++ timeout--; ++ } ++ ++ if (timeout <= 0) { ++ /* If PDEN is set, we can get a timeout when pen goes up */ ++ if (is_pden(wm)) ++ wm->pen_probably_down = 0; ++ else ++ dbg ("adc sample timeout"); ++ return RC_PENUP; ++ } ++ ++ *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (wm->mach_ops && wm->mach_ops->post_sample) ++ wm->mach_ops->post_sample(adcsel); ++ ++ /* check we have correct sample */ ++ if ((*sample & WM97XX_ADCSEL_MASK) != adcsel) { ++ dbg ("adc wrong sample, read %x got %x", adcsel, ++ *sample & WM97XX_ADCSEL_MASK); ++ return RC_PENUP; ++ } ++ ++ if (!(*sample & WM97XX_PEN_DOWN)) { ++ wm->pen_probably_down = 0; ++ return RC_PENUP; ++ } ++ ++ return RC_VALID; ++} ++ ++/* ++ * Sample the WM9705 touchscreen in polling mode ++ */ ++static int wm9705_poll_touch(struct wm97xx* wm, struct wm97xx_data *data) ++{ ++ int rc; ++ ++ if ((rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_X, &data->x)) != RC_VALID) ++ return rc; ++ if ((rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_Y, &data->y)) != RC_VALID) ++ return rc; ++ if (pil) { ++ if ((rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_PRES, &data->p)) != RC_VALID) ++ return rc; ++ } else ++ data->p = DEFAULT_PRESSURE; ++ ++ return RC_VALID; ++} ++ ++/* ++ * Enable WM9705 continuous mode, i.e. touch data is streamed across an AC97 slot ++ */ ++static int wm9705_acc_enable (struct wm97xx* wm, int enable) ++{ ++ u16 dig1, dig2; ++ int ret = 0; ++ ++ dig1 = wm->dig[1]; ++ dig2 = wm->dig[2]; ++ ++ if (enable) { ++ /* continous mode */ ++ if (wm->mach_ops->acc_startup && (ret = wm->mach_ops->acc_startup(wm)) < 0) ++ return ret; ++ dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK | ++ WM97XX_DELAY_MASK | WM97XX_SLT_MASK); ++ dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN | ++ WM97XX_DELAY (delay) | ++ WM97XX_SLT (wm->acc_slot) | ++ WM97XX_RATE (wm->acc_rate); ++ if (pil) ++ dig1 |= WM97XX_ADCSEL_PRES; ++ dig2 |= WM9705_PDEN; ++ } else { ++ dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN); ++ dig2 &= ~WM9705_PDEN; ++ if (wm->mach_ops->acc_shutdown) ++ wm->mach_ops->acc_shutdown(wm); ++ } ++ ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2); ++ return ret; ++} ++ ++struct wm97xx_codec_drv wm97xx_codec = { ++ .id = WM9705_ID2, ++ .name = "wm9705", ++ .poll_sample = wm9705_poll_sample, ++ .poll_touch = wm9705_poll_touch, ++ .acc_enable = wm9705_acc_enable, ++ .digitiser_ioctl = wm9705_digitiser_ioctl, ++}; ++ ++EXPORT_SYMBOL_GPL(wm97xx_codec); ++ ++/* Module information */ ++MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); ++MODULE_DESCRIPTION("WM9705 Touch Screen Driver"); ++MODULE_LICENSE("GPL"); +Index: linux-2.6.17/drivers/input/touchscreen/wm9712.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.17/drivers/input/touchscreen/wm9712.c 2006-09-19 20:36:47.969052000 +0200 +@@ -0,0 +1,464 @@ ++/* ++ * wm9712.c -- Codec driver for Wolfson WM9712 AC97 Codecs. ++ * ++ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC. ++ * Author: Liam Girdwood ++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com ++ * Parts Copyright : Ian Molton <spyro@f2s.com> ++ * Andrew Zabolotny <zap@homelink.ru> ++ * Russell King <rmk@arm.linux.org.uk> ++ * ++ * 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 the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * Revision history ++ * 4th Jul 2005 Initial version. ++ * 6th Sep 2006 Mike Arthur <linux@wolfsonmicro.com> ++ * Added pre and post sample calls. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/version.h> ++#include <linux/kernel.h> ++#include <linux/input.h> ++#include <linux/delay.h> ++#include <linux/bitops.h> ++#include <linux/wm97xx.h> ++ ++#define TS_NAME "wm97xx" ++#define WM9712_VERSION "0.61" ++#define DEFAULT_PRESSURE 0xb0c0 ++ ++/* ++ * Debug ++ */ ++#if 0 ++#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg) ++#else ++#define dbg(format, arg...) ++#endif ++#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg) ++#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg) ++#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg) ++ ++/* ++ * Module parameters ++ */ ++ ++/* ++ * Set internal pull up for pen detect. ++ * ++ * Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive) ++ * i.e. pull up resistance = 64k Ohms / rpu. ++ * ++ * Adjust this value if you are having problems with pen detect not ++ * detecting any down event. ++ */ ++static int rpu = 3; ++module_param(rpu, int, 0); ++MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect."); ++ ++/* ++ * Set current used for pressure measurement. ++ * ++ * Set pil = 2 to use 400uA ++ * pil = 1 to use 200uA and ++ * pil = 0 to disable pressure measurement. ++ * ++ * This is used to increase the range of values returned by the adc ++ * when measureing touchpanel pressure. ++ */ ++static int pil = 0; ++module_param(pil, int, 0); ++MODULE_PARM_DESC(pil, "Set current used for pressure measurement."); ++ ++/* ++ * Set threshold for pressure measurement. ++ * ++ * Pen down pressure below threshold is ignored. ++ */ ++static int pressure = DEFAULT_PRESSURE & 0xfff; ++module_param(pressure, int, 0); ++MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement."); ++ ++/* ++ * Set adc sample delay. ++ * ++ * For accurate touchpanel measurements, some settling time may be ++ * required between the switch matrix applying a voltage across the ++ * touchpanel plate and the ADC sampling the signal. ++ * ++ * This delay can be set by setting delay = n, where n is the array ++ * position of the delay in the array delay_table below. ++ * Long delays > 1ms are supported for completeness, but are not ++ * recommended. ++ */ ++static int delay = 3; ++module_param(delay, int, 0); ++MODULE_PARM_DESC(delay, "Set adc sample delay."); ++ ++/* ++ * Set five_wire = 1 to use a 5 wire touchscreen. ++ * ++ * NOTE: Five wire mode does not allow for readback of pressure. ++ */ ++static int five_wire; ++module_param(five_wire, int, 0); ++MODULE_PARM_DESC(five_wire, "Set to '1' to use 5-wire touchscreen."); ++ ++/* ++ * Set adc mask function. ++ * ++ * Sources of glitch noise, such as signals driving an LCD display, may feed ++ * through to the touch screen plates and affect measurement accuracy. In ++ * order to minimise this, a signal may be applied to the MASK pin to delay or ++ * synchronise the sampling. ++ * ++ * 0 = No delay or sync ++ * 1 = High on pin stops conversions ++ * 2 = Edge triggered, edge on pin delays conversion by delay param (above) ++ * 3 = Edge triggered, edge on pin starts conversion after delay param ++ */ ++static int mask = 0; ++module_param(mask, int, 0); ++MODULE_PARM_DESC(mask, "Set adc mask function."); ++ ++/* ++ * Coordinate Polling Enable. ++ * ++ * Set to 1 to enable coordinate polling. e.g. x,y[,p] is sampled together ++ * for every poll. ++ */ ++static int coord = 0; ++module_param(coord, int, 0); ++MODULE_PARM_DESC(coord, "Polling coordinate mode"); ++ ++/* ++ * ADC sample delay times in uS ++ */ ++static const int delay_table[] = { ++ 21, // 1 AC97 Link frames ++ 42, // 2 ++ 84, // 4 ++ 167, // 8 ++ 333, // 16 ++ 667, // 32 ++ 1000, // 48 ++ 1333, // 64 ++ 2000, // 96 ++ 2667, // 128 ++ 3333, // 160 ++ 4000, // 192 ++ 4667, // 224 ++ 5333, // 256 ++ 6000, // 288 ++ 0 // No delay, switch matrix always on ++}; ++ ++/* ++ * Delay after issuing a POLL command. ++ * ++ * The delay is 3 AC97 link frames + the touchpanel settling delay ++ */ ++static inline void poll_delay(int d) ++{ ++ udelay (3 * AC97_LINK_FRAME + delay_table [d]); ++} ++ ++/* ++ * set up the physical settings of the WM9712 ++ */ ++static void init_wm9712_phy(struct wm97xx* wm) ++{ ++ u16 dig1 = 0; ++ u16 dig2 = WM97XX_RPR | WM9712_RPU(1); ++ ++ /* WM9712 rpu */ ++ if (rpu) { ++ dig2 &= 0xffc0; ++ dig2 |= WM9712_RPU(rpu); ++ dbg("setting pen detect pull-up to %d Ohms",64000 / rpu); ++ } ++ ++ /* touchpanel pressure current*/ ++ if (pil == 2) { ++ dig2 |= WM9712_PIL; ++ dbg("setting pressure measurement current to 400uA."); ++ } else if (pil) ++ dbg("setting pressure measurement current to 200uA."); ++ if(!pil) ++ pressure = 0; ++ ++ /* WM9712 five wire */ ++ if (five_wire) { ++ dig2 |= WM9712_45W; ++ dbg("setting 5-wire touchscreen mode."); ++ } ++ ++ /* polling mode sample settling delay */ ++ if (delay < 0 || delay > 15) { ++ dbg("supplied delay out of range."); ++ delay = 4; ++ } ++ dig1 &= 0xff0f; ++ dig1 |= WM97XX_DELAY(delay); ++ dbg("setting adc sample delay to %d u Secs.", delay_table[delay]); ++ ++ /* mask */ ++ dig2 |= ((mask & 0x3) << 6); ++ if (mask) { ++ u16 reg; ++ /* Set GPIO4 as Mask Pin*/ ++ reg = wm97xx_reg_read(wm, AC97_MISC_AFE); ++ wm97xx_reg_write(wm, AC97_MISC_AFE, reg | WM97XX_GPIO_4); ++ reg = wm97xx_reg_read(wm, AC97_GPIO_CFG); ++ wm97xx_reg_write(wm, AC97_GPIO_CFG, reg | WM97XX_GPIO_4); ++ } ++ ++ /* wait - coord mode */ ++ if(coord) ++ dig2 |= WM9712_WAIT; ++ ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2); ++} ++ ++static int wm9712_digitiser_ioctl(struct wm97xx* wm, int cmd) ++{ ++ u16 dig2 = wm->dig[2]; ++ ++ switch(cmd) { ++ case WM97XX_DIG_START: ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2 | WM97XX_PRP_DET_DIG); ++ wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */ ++ break; ++ case WM97XX_DIG_STOP: ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2 & ~WM97XX_PRP_DET_DIG); ++ break; ++ case WM97XX_AUX_PREPARE: ++ memcpy(wm->dig_save, wm->dig, sizeof(wm->dig)); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 0); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG); ++ break; ++ case WM97XX_DIG_RESTORE: ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]); ++ break; ++ case WM97XX_PHY_INIT: ++ init_wm9712_phy(wm); ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static inline int is_pden (struct wm97xx* wm) ++{ ++ return wm->dig[2] & WM9712_PDEN; ++} ++ ++/* ++ * Read a sample from the WM9712 adc in polling mode. ++ */ ++static int wm9712_poll_sample (struct wm97xx* wm, int adcsel, int *sample) ++{ ++ int timeout = 5 * delay; ++ ++ if (!wm->pen_probably_down) { ++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (!(data & WM97XX_PEN_DOWN)) ++ return RC_PENUP; ++ wm->pen_probably_down = 1; ++ } ++ ++ /* set up digitiser */ ++ if (adcsel & 0x8000) ++ adcsel = ((adcsel & 0x7fff) + 3) << 12; ++ ++ if (wm->mach_ops && wm->mach_ops->pre_sample) ++ wm->mach_ops->pre_sample(adcsel); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, adcsel | WM97XX_POLL | WM97XX_DELAY(delay)); ++ ++ /* wait 3 AC97 time slots + delay for conversion */ ++ poll_delay (delay); ++ ++ /* wait for POLL to go low */ ++ while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) { ++ udelay(AC97_LINK_FRAME); ++ timeout--; ++ } ++ ++ if (timeout <= 0) { ++ /* If PDEN is set, we can get a timeout when pen goes up */ ++ if (is_pden(wm)) ++ wm->pen_probably_down = 0; ++ else ++ dbg ("adc sample timeout"); ++ return RC_PENUP; ++ } ++ ++ *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (wm->mach_ops && wm->mach_ops->post_sample) ++ wm->mach_ops->post_sample(adcsel); ++ ++ /* check we have correct sample */ ++ if ((*sample & WM97XX_ADCSEL_MASK) != adcsel) { ++ dbg ("adc wrong sample, read %x got %x", adcsel, ++ *sample & WM97XX_ADCSEL_MASK); ++ return RC_PENUP; ++ } ++ ++ if (!(*sample & WM97XX_PEN_DOWN)) { ++ wm->pen_probably_down = 0; ++ return RC_PENUP; ++ } ++ ++ return RC_VALID; ++} ++ ++/* ++ * Read a coord from the WM9712 adc in polling mode. ++ */ ++static int wm9712_poll_coord (struct wm97xx* wm, struct wm97xx_data *data) ++{ ++ int timeout = 5 * delay; ++ ++ if (!wm->pen_probably_down) { ++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (!(data & WM97XX_PEN_DOWN)) ++ return RC_PENUP; ++ wm->pen_probably_down = 1; ++ } ++ ++ /* set up digitiser */ ++ if (wm->mach_ops && wm->mach_ops->pre_sample) ++ wm->mach_ops->pre_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y); ++ ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, ++ WM97XX_COO | WM97XX_POLL | WM97XX_DELAY(delay)); ++ ++ /* wait 3 AC97 time slots + delay for conversion and read x */ ++ poll_delay(delay); ++ data->x = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ /* wait for POLL to go low */ ++ while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) { ++ udelay(AC97_LINK_FRAME); ++ timeout--; ++ } ++ ++ if (timeout <= 0) { ++ /* If PDEN is set, we can get a timeout when pen goes up */ ++ if (is_pden(wm)) ++ wm->pen_probably_down = 0; ++ else ++ dbg ("adc sample timeout"); ++ return RC_PENUP; ++ } ++ ++ /* read back y data */ ++ data->y = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (pil) ++ data->p = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ else ++ data->p = DEFAULT_PRESSURE; ++ ++ if (wm->mach_ops && wm->mach_ops->post_sample) ++ wm->mach_ops->post_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y); ++ ++ /* check we have correct sample */ ++ if (!(data->x & WM97XX_ADCSEL_X) || !(data->y & WM97XX_ADCSEL_Y)) ++ goto err; ++ if(pil && !(data->p & WM97XX_ADCSEL_PRES)) ++ goto err; ++ ++ if (!(data->x & WM97XX_PEN_DOWN)) { ++ wm->pen_probably_down = 0; ++ return RC_PENUP; ++ } ++ return RC_VALID; ++err: ++ return RC_PENUP; ++} ++ ++/* ++ * Sample the WM9712 touchscreen in polling mode ++ */ ++static int wm9712_poll_touch(struct wm97xx* wm, struct wm97xx_data *data) ++{ ++ int rc; ++ ++ if(coord) { ++ if((rc = wm9712_poll_coord(wm, data)) != RC_VALID) ++ return rc; ++ } else { ++ if ((rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_X, &data->x)) != RC_VALID) ++ return rc; ++ ++ if ((rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_Y, &data->y)) != RC_VALID) ++ return rc; ++ ++ if (pil && !five_wire) { ++ if ((rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_PRES, &data->p)) != RC_VALID) ++ return rc; ++ } else ++ data->p = DEFAULT_PRESSURE; ++ } ++ return RC_VALID; ++} ++ ++/* ++ * Enable WM9712 continuous mode, i.e. touch data is streamed across an AC97 slot ++ */ ++static int wm9712_acc_enable (struct wm97xx* wm, int enable) ++{ ++ u16 dig1, dig2; ++ int ret = 0; ++ ++ dig1 = wm->dig[1]; ++ dig2 = wm->dig[2]; ++ ++ if (enable) { ++ /* continous mode */ ++ if (wm->mach_ops->acc_startup && (ret = wm->mach_ops->acc_startup(wm)) < 0) ++ return ret; ++ dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK | ++ WM97XX_DELAY_MASK | WM97XX_SLT_MASK); ++ dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN | ++ WM97XX_DELAY (delay) | ++ WM97XX_SLT (wm->acc_slot) | ++ WM97XX_RATE (wm->acc_rate); ++ if (pil) ++ dig1 |= WM97XX_ADCSEL_PRES; ++ dig2 |= WM9712_PDEN; ++ } else { ++ dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN); ++ dig2 &= ~WM9712_PDEN; ++ if (wm->mach_ops->acc_shutdown) ++ wm->mach_ops->acc_shutdown(wm); ++ } ++ ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2); ++ return 0; ++} ++ ++struct wm97xx_codec_drv wm97xx_codec = { ++ .id = WM9712_ID2, ++ .name = "wm9712", ++ .poll_sample = wm9712_poll_sample, ++ .poll_touch = wm9712_poll_touch, ++ .acc_enable = wm9712_acc_enable, ++ .digitiser_ioctl = wm9712_digitiser_ioctl, ++}; ++ ++EXPORT_SYMBOL_GPL(wm97xx_codec); ++ ++/* Module information */ ++MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); ++MODULE_DESCRIPTION("WM9712 Touch Screen Driver"); ++MODULE_LICENSE("GPL"); +Index: linux-2.6.17/drivers/input/touchscreen/wm9713.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.17/drivers/input/touchscreen/wm9713.c 2006-09-19 20:36:47.969052000 +0200 +@@ -0,0 +1,461 @@ ++/* ++ * wm9713.c -- Codec touch driver for Wolfson WM9713 AC97 Codec. ++ * ++ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC. ++ * Author: Liam Girdwood ++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com ++ * Parts Copyright : Ian Molton <spyro@f2s.com> ++ * Andrew Zabolotny <zap@homelink.ru> ++ * Russell King <rmk@arm.linux.org.uk> ++ * ++ * 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 the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * Revision history ++ * 6th Sep 2006 Mike Arthur <linux@wolfsonmicro.com> ++ * Added pre and post sample calls. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/version.h> ++#include <linux/kernel.h> ++#include <linux/input.h> ++#include <linux/delay.h> ++#include <linux/bitops.h> ++#include <linux/wm97xx.h> ++ ++#define TS_NAME "wm97xx" ++#define WM9713_VERSION "0.53" ++#define DEFAULT_PRESSURE 0xb0c0 ++ ++/* ++ * Debug ++ */ ++#if 0 ++#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg) ++#else ++#define dbg(format, arg...) ++#endif ++#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg) ++#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg) ++#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg) ++ ++/* ++ * Module parameters ++ */ ++ ++/* ++ * Set internal pull up for pen detect. ++ * ++ * Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive) ++ * i.e. pull up resistance = 64k Ohms / rpu. ++ * ++ * Adjust this value if you are having problems with pen detect not ++ * detecting any down event. ++ */ ++static int rpu = 1; ++module_param(rpu, int, 0); ++MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect."); ++ ++/* ++ * Set current used for pressure measurement. ++ * ++ * Set pil = 2 to use 400uA ++ * pil = 1 to use 200uA and ++ * pil = 0 to disable pressure measurement. ++ * ++ * This is used to increase the range of values returned by the adc ++ * when measureing touchpanel pressure. ++ */ ++static int pil = 0; ++module_param(pil, int, 0); ++MODULE_PARM_DESC(pil, "Set current used for pressure measurement."); ++ ++/* ++ * Set threshold for pressure measurement. ++ * ++ * Pen down pressure below threshold is ignored. ++ */ ++static int pressure = DEFAULT_PRESSURE & 0xfff; ++module_param(pressure, int, 0); ++MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement."); ++ ++/* ++ * Set adc sample delay. ++ * ++ * For accurate touchpanel measurements, some settling time may be ++ * required between the switch matrix applying a voltage across the ++ * touchpanel plate and the ADC sampling the signal. ++ * ++ * This delay can be set by setting delay = n, where n is the array ++ * position of the delay in the array delay_table below. ++ * Long delays > 1ms are supported for completeness, but are not ++ * recommended. ++ */ ++static int delay = 4; ++module_param(delay, int, 0); ++MODULE_PARM_DESC(delay, "Set adc sample delay."); ++ ++/* ++ * Set adc mask function. ++ * ++ * Sources of glitch noise, such as signals driving an LCD display, may feed ++ * through to the touch screen plates and affect measurement accuracy. In ++ * order to minimise this, a signal may be applied to the MASK pin to delay or ++ * synchronise the sampling. ++ * ++ * 0 = No delay or sync ++ * 1 = High on pin stops conversions ++ * 2 = Edge triggered, edge on pin delays conversion by delay param (above) ++ * 3 = Edge triggered, edge on pin starts conversion after delay param ++ */ ++static int mask = 0; ++module_param(mask, int, 0); ++MODULE_PARM_DESC(mask, "Set adc mask function."); ++ ++/* ++ * Coordinate Polling Enable. ++ * ++ * Set to 1 to enable coordinate polling. e.g. x,y[,p] is sampled together ++ * for every poll. ++ */ ++static int coord = 1; ++module_param(coord, int, 0); ++MODULE_PARM_DESC(coord, "Polling coordinate mode"); ++ ++/* ++ * ADC sample delay times in uS ++ */ ++static const int delay_table[] = { ++ 21, // 1 AC97 Link frames ++ 42, // 2 ++ 84, // 4 ++ 167, // 8 ++ 333, // 16 ++ 667, // 32 ++ 1000, // 48 ++ 1333, // 64 ++ 2000, // 96 ++ 2667, // 128 ++ 3333, // 160 ++ 4000, // 192 ++ 4667, // 224 ++ 5333, // 256 ++ 6000, // 288 ++ 0 // No delay, switch matrix always on ++}; ++ ++/* ++ * Delay after issuing a POLL command. ++ * ++ * The delay is 3 AC97 link frames + the touchpanel settling delay ++ */ ++static inline void poll_delay(int d) ++{ ++ udelay (3 * AC97_LINK_FRAME + delay_table [d]); ++} ++ ++/* ++ * set up the physical settings of the WM9713 ++ */ ++static void init_wm9713_phy(struct wm97xx* wm) ++{ ++ u16 dig1 = 0, dig2, dig3; ++ ++ /* default values */ ++ dig2 = WM97XX_DELAY(4) | WM97XX_SLT(5); ++ dig3= WM9712_RPU(1); ++ ++ /* rpu */ ++ if (rpu) { ++ dig3 &= 0xffc0; ++ dig3 |= WM9712_RPU(rpu); ++ info("setting pen detect pull-up to %d Ohms",64000 / rpu); ++ } ++ ++ /* touchpanel pressure */ ++ if (pil == 2) { ++ dig3 |= WM9712_PIL; ++ info("setting pressure measurement current to 400uA."); ++ } else if (pil) ++ info ("setting pressure measurement current to 200uA."); ++ if(!pil) ++ pressure = 0; ++ ++ /* sample settling delay */ ++ if (delay < 0 || delay > 15) { ++ info ("supplied delay out of range."); ++ delay = 4; ++ info("setting adc sample delay to %d u Secs.", delay_table[delay]); ++ } ++ dig2 &= 0xff0f; ++ dig2 |= WM97XX_DELAY(delay); ++ ++ /* mask */ ++ dig3 |= ((mask & 0x3) << 4); ++ if(coord) ++ dig3 |= WM9713_WAIT; ++ ++ wm->misc = wm97xx_reg_read(wm, 0x5a); ++ ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, dig2); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, dig3); ++ wm97xx_reg_write(wm, AC97_GPIO_STICKY, 0x0); ++} ++ ++static int wm9713_digitiser_ioctl(struct wm97xx* wm, int cmd) ++{ ++ u16 val = 0; ++ ++ switch(cmd){ ++ case WM97XX_DIG_START: ++ val = wm97xx_reg_read(wm, AC97_EXTENDED_MID); ++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, val & 0x7fff); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2] | WM97XX_PRP_DET_DIG); ++ wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */ ++ break; ++ case WM97XX_DIG_STOP: ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2] & ~WM97XX_PRP_DET_DIG); ++ val = wm97xx_reg_read(wm, AC97_EXTENDED_MID); ++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, val | 0x8000); ++ break; ++ case WM97XX_AUX_PREPARE: ++ memcpy(wm->dig_save, wm->dig, sizeof(wm->dig)); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, 0); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, 0); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, WM97XX_PRP_DET_DIG); ++ break; ++ case WM97XX_DIG_RESTORE: ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, wm->dig_save[0]); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, wm->dig_save[1]); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig_save[2]); ++ break; ++ case WM97XX_PHY_INIT: ++ init_wm9713_phy(wm); ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static inline int is_pden (struct wm97xx* wm) ++{ ++ return wm->dig[2] & WM9713_PDEN; ++} ++ ++/* ++ * Read a sample from the WM9713 adc in polling mode. ++ */ ++static int wm9713_poll_sample (struct wm97xx* wm, int adcsel, int *sample) ++{ ++ u16 dig1; ++ int timeout = 5 * delay; ++ ++ if (!wm->pen_probably_down) { ++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (!(data & WM97XX_PEN_DOWN)) ++ return RC_PENUP; ++ wm->pen_probably_down = 1; ++ } ++ ++ /* set up digitiser */ ++ if (adcsel & 0x8000) ++ adcsel = 1 << ((adcsel & 0x7fff) + 3); ++ ++ dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1); ++ dig1 &= ~WM9713_ADCSEL_MASK; ++ ++ if (wm->mach_ops && wm->mach_ops->pre_sample) ++ wm->mach_ops->pre_sample(adcsel); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1 | adcsel |WM9713_POLL); ++ ++ /* wait 3 AC97 time slots + delay for conversion */ ++ poll_delay(delay); ++ ++ /* wait for POLL to go low */ ++ while ((wm97xx_reg_read(wm, AC97_WM9713_DIG1) & WM9713_POLL) && timeout) { ++ udelay(AC97_LINK_FRAME); ++ timeout--; ++ } ++ ++ if (timeout <= 0) { ++ /* If PDEN is set, we can get a timeout when pen goes up */ ++ if (is_pden(wm)) ++ wm->pen_probably_down = 0; ++ else ++ dbg ("adc sample timeout"); ++ return RC_PENUP; ++ } ++ ++ *sample =wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (wm->mach_ops && wm->mach_ops->post_sample) ++ wm->mach_ops->post_sample(adcsel); ++ ++ /* check we have correct sample */ ++ if ((*sample & WM97XX_ADCSRC_MASK) != ffs(adcsel >> 1) << 12) { ++ dbg ("adc wrong sample, read %x got %x", adcsel, ++ *sample & WM97XX_ADCSRC_MASK); ++ return RC_PENUP; ++ } ++ ++ if (!(*sample & WM97XX_PEN_DOWN)) { ++ wm->pen_probably_down = 0; ++ return RC_PENUP; ++ } ++ ++ return RC_VALID; ++} ++ ++/* ++ * Read a coordinate from the WM9713 adc in polling mode. ++ */ ++static int wm9713_poll_coord (struct wm97xx* wm, struct wm97xx_data *data) ++{ ++ u16 dig1; ++ int timeout = 5 * delay; ++ ++ if (!wm->pen_probably_down) { ++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (!(data & WM97XX_PEN_DOWN)) ++ return RC_PENUP; ++ wm->pen_probably_down = 1; ++ } ++ ++ /* set up digitiser */ ++ dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1); ++ dig1 &= ~WM9713_ADCSEL_MASK; ++ if(pil) ++ dig1 |= WM97XX_ADCSEL_PRES; ++ ++ if (wm->mach_ops && wm->mach_ops->pre_sample) ++ wm->mach_ops->pre_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1 | WM9713_POLL | WM9713_COO); ++ ++ /* wait 3 AC97 time slots + delay for conversion */ ++ poll_delay(delay); ++ data->x = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ /* wait for POLL to go low */ ++ while ((wm97xx_reg_read(wm, AC97_WM9713_DIG1) & WM9713_POLL) && timeout) { ++ udelay(AC97_LINK_FRAME); ++ timeout--; ++ } ++ ++ if (timeout <= 0) { ++ /* If PDEN is set, we can get a timeout when pen goes up */ ++ if (is_pden(wm)) ++ wm->pen_probably_down = 0; ++ else ++ dbg ("adc sample timeout"); ++ return RC_PENUP; ++ } ++ ++ /* read back data */ ++ data->y = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (pil) ++ data->p = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ else ++ data->p = DEFAULT_PRESSURE; ++ ++ if (wm->mach_ops && wm->mach_ops->post_sample) ++ wm->mach_ops->post_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y); ++ ++ /* check we have correct sample */ ++ if (!(data->x & WM97XX_ADCSEL_X) || !(data->y & WM97XX_ADCSEL_Y)) ++ goto err; ++ if(pil && !(data->p & WM97XX_ADCSEL_PRES)) ++ goto err; ++ ++ if (!(data->x & WM97XX_PEN_DOWN)) { ++ wm->pen_probably_down = 0; ++ return RC_PENUP; ++ } ++ return RC_VALID; ++err: ++ return RC_PENUP; ++} ++ ++/* ++ * Sample the WM9713 touchscreen in polling mode ++ */ ++static int wm9713_poll_touch(struct wm97xx* wm, struct wm97xx_data *data) ++{ ++ int rc; ++ ++ if(coord) { ++ if((rc = wm9713_poll_coord(wm, data)) != RC_VALID) ++ return rc; ++ } else { ++ if ((rc = wm9713_poll_sample(wm, WM9713_ADCSEL_X, &data->x)) != RC_VALID) ++ return rc; ++ if ((rc = wm9713_poll_sample(wm, WM9713_ADCSEL_Y, &data->y)) != RC_VALID) ++ return rc; ++ if (pil) { ++ if ((rc = wm9713_poll_sample(wm, WM9713_ADCSEL_PRES, &data->p)) != RC_VALID) ++ return rc; ++ } else ++ data->p = DEFAULT_PRESSURE; ++ } ++ return RC_VALID; ++} ++ ++/* ++ * Enable WM9713 continuous mode, i.e. touch data is streamed across an AC97 slot ++ */ ++static int wm9713_acc_enable (struct wm97xx* wm, int enable) ++{ ++ u16 dig1, dig2, dig3; ++ int ret = 0; ++ ++ dig1 = wm->dig[0]; ++ dig2 = wm->dig[1]; ++ dig3 = wm->dig[2]; ++ ++ if (enable) { ++ /* continous mode */ ++ if (wm->mach_ops->acc_startup && ++ (ret = wm->mach_ops->acc_startup(wm)) < 0) ++ return ret; ++ ++ dig1 &= ~WM9713_ADCSEL_MASK; ++ dig1 |= WM9713_CTC | WM9713_COO | WM9713_ADCSEL_X | WM9713_ADCSEL_Y; ++ if (pil) ++ dig1 |= WM9713_ADCSEL_PRES; ++ dig2 &= ~(WM97XX_DELAY_MASK | WM97XX_SLT_MASK | WM97XX_CM_RATE_MASK); ++ dig2 |= WM97XX_SLEN | WM97XX_DELAY (delay) | ++ WM97XX_SLT (wm->acc_slot) | WM97XX_RATE (wm->acc_rate); ++ dig3 |= WM9713_PDEN; ++ } else { ++ dig1 &= ~(WM9713_CTC | WM9713_COO); ++ dig2 &= ~WM97XX_SLEN; ++ dig3 &= ~WM9713_PDEN; ++ if (wm->mach_ops->acc_shutdown) ++ wm->mach_ops->acc_shutdown(wm); ++ } ++ ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, dig2); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, dig3); ++ return ret; ++} ++ ++struct wm97xx_codec_drv wm97xx_codec = { ++ .id = WM9713_ID2, ++ .name = "wm9713", ++ .poll_sample = wm9713_poll_sample, ++ .poll_touch = wm9713_poll_touch, ++ .acc_enable = wm9713_acc_enable, ++ .digitiser_ioctl = wm9713_digitiser_ioctl, ++}; ++ ++EXPORT_SYMBOL_GPL(wm97xx_codec); ++ ++/* Module information */ ++MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); ++MODULE_DESCRIPTION("WM9713 Touch Screen Driver"); ++MODULE_LICENSE("GPL"); +Index: linux-2.6.17/drivers/input/touchscreen/wm97xx-core.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.17/drivers/input/touchscreen/wm97xx-core.c 2006-09-19 20:36:47.969052000 +0200 +@@ -0,0 +1,912 @@ ++/* ++ * wm97xx-core.c -- Touch screen driver core for Wolfson WM9705, WM9712 ++ * and WM9713 AC97 Codecs. ++ * ++ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC. ++ * Author: Liam Girdwood ++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com ++ * Parts Copyright : Ian Molton <spyro@f2s.com> ++ * Andrew Zabolotny <zap@homelink.ru> ++ * Russell King <rmk@arm.linux.org.uk> ++ * ++ * 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 the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * Notes: ++ * ++ * Features: ++ * - supports WM9705, WM9712, WM9713 ++ * - polling mode ++ * - continuous mode (arch-dependent) ++ * - adjustable rpu/dpp settings ++ * - adjustable pressure current ++ * - adjustable sample settle delay ++ * - 4 and 5 wire touchscreens (5 wire is WM9712 only) ++ * - pen down detection ++ * - battery monitor ++ * - sample AUX adc's ++ * - power management ++ * - codec GPIO ++ * - codec event notification ++ * Todo ++ * - Support for async sampling control for noisy LCD's. ++ * ++ * Revision history ++ * 7th May 2003 Initial version. ++ * 6th June 2003 Added non module support and AC97 registration. ++ * 18th June 2003 Added AUX adc sampling. ++ * 23rd June 2003 Did some minimal reformatting, fixed a couple of ++ * codec_mutexing bugs and noted a race to fix. ++ * 24th June 2003 Added power management and fixed race condition. ++ * 10th July 2003 Changed to a misc device. ++ * 31st July 2003 Moved TS_EVENT and TS_CAL to wm97xx.h ++ * 8th Aug 2003 Added option for read() calling wm97xx_sample_touch() ++ * because some ac97_read/ac_97_write call schedule() ++ * 7th Nov 2003 Added Input touch event interface, stanley.cai@intel.com ++ * 13th Nov 2003 Removed h3600 touch interface, added interrupt based ++ * pen down notification and implemented continous mode ++ * on XScale arch. ++ * 16th Nov 2003 Ian Molton <spyro@f2s.com> ++ * Modified so that it suits the new 2.6 driver model. ++ * 25th Jan 2004 Andrew Zabolotny <zap@homelink.ru> ++ * Implemented IRQ-driven pen down detection, implemented ++ * the private API meant to be exposed to platform-specific ++ * drivers, reorganized the driver so that it supports ++ * an arbitrary number of devices. ++ * 1st Feb 2004 Moved continuous mode handling to a separate ++ * architecture-dependent file. For now only PXA ++ * built-in AC97 controller is supported (pxa-ac97-wm97xx.c). ++ * 11th Feb 2004 Reduced CPU usage by keeping a cached copy of both ++ * digitizer registers instead of reading them every time. ++ * A reorganization of the whole code for better ++ * error handling. ++ * 17th Apr 2004 Added BMON support. ++ * 17th Nov 2004 Added codec GPIO, codec event handling (real and virtual ++ * GPIOs) and 2.6 power management. ++ * 29th Nov 2004 Added WM9713 support. ++ * 4th Jul 2005 Moved codec specific code out to seperate files. ++ * 6th Sep 2006 Mike Arthur <linux@wolfsonmicro.com> ++ * Added bus interface. ++ */ ++ ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/version.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/delay.h> ++#include <linux/string.h> ++#include <linux/proc_fs.h> ++#include <linux/pm.h> ++#include <linux/interrupt.h> ++#include <linux/bitops.h> ++#include <linux/workqueue.h> ++#include <linux/device.h> ++#include <linux/wm97xx.h> ++#include <asm/uaccess.h> ++#include <asm/io.h> ++ ++#define TS_NAME "wm97xx" ++#define WM_CORE_VERSION "0.63" ++#define DEFAULT_PRESSURE 0xb0c0 ++ ++/* ++ * WM97xx - enable/disable AUX ADC sysfs ++ */ ++static int aux_sys = 1; ++module_param(aux_sys, int, 0); ++MODULE_PARM_DESC(aux_sys, "enable AUX ADC sysfs entries"); ++ ++/* ++ * WM97xx - enable/disable codec status sysfs ++ */ ++static int status_sys = 1; ++module_param(status_sys, int, 0); ++MODULE_PARM_DESC(status_sys, "enable codec status sysfs entries"); ++ ++/* ++ * Touchscreen absolute values ++ * ++ * These parameters are used to help the input layer discard out of ++ * range readings and reduce jitter etc. ++ * ++ * o min, max:- indicate the min and max values your touch screen returns ++ * o fuzz:- use a higher number to reduce jitter ++ * ++ * The default values correspond to Mainstone II in QVGA mode ++ * ++ * Please read ++ * Documentation/input/input-programming.txt for more details. ++ */ ++ ++static int abs_x[3] = {350,3900,5}; ++module_param_array(abs_x, int, NULL, 0); ++MODULE_PARM_DESC(abs_x, "Touchscreen absolute X min, max, fuzz"); ++ ++static int abs_y[3] = {320,3750,40}; ++module_param_array(abs_y, int, NULL, 0); ++MODULE_PARM_DESC(abs_y, "Touchscreen absolute Y min, max, fuzz"); ++ ++static int abs_p[3] = {0,150,4}; ++module_param_array(abs_p, int, NULL, 0); ++MODULE_PARM_DESC(abs_p, "Touchscreen absolute Pressure min, max, fuzz"); ++ ++/* ++ * Debug ++ */ ++#if 0 ++#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg) ++#else ++#define dbg(format, arg...) ++#endif ++#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg) ++#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg) ++#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg) ++ ++/* codec AC97 IO access */ ++int wm97xx_reg_read(struct wm97xx *wm, u16 reg) ++{ ++ if (wm->ac97) ++ return wm->ac97->bus->ops->read(wm->ac97, reg); ++ else ++ return -1; ++} ++ ++void wm97xx_reg_write(struct wm97xx *wm, u16 reg, u16 val) ++{ ++ /* cache digitiser registers */ ++ if(reg >= AC97_WM9713_DIG1 && reg <= AC97_WM9713_DIG3) ++ wm->dig[(reg - AC97_WM9713_DIG1) >> 1] = val; ++ ++ /* cache gpio regs */ ++ if(reg >= AC97_GPIO_CFG && reg <= AC97_MISC_AFE) ++ wm->gpio[(reg - AC97_GPIO_CFG) >> 1] = val; ++ ++ /* wm9713 irq reg */ ++ if(reg == 0x5a) ++ wm->misc = val; ++ ++ if (wm->ac97) ++ wm->ac97->bus->ops->write(wm->ac97, reg, val); ++} ++ ++ ++/** ++ * wm97xx_read_aux_adc - Read the aux adc. ++ * @wm: wm97xx device. ++ * @adcsel: codec ADC to be read ++ * ++ * Reads the selected AUX ADC. ++ */ ++ ++int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel) ++{ ++ int power_adc = 0, auxval; ++ u16 power = 0; ++ ++ /* get codec */ ++ mutex_lock(&wm->codec_mutex); ++ ++ /* When the touchscreen is not in use, we may have to power up the AUX ADC ++ * before we can use sample the AUX inputs-> ++ */ ++ if (wm->id == WM9713_ID2 && ++ (power = wm97xx_reg_read(wm, AC97_EXTENDED_MID)) & 0x8000) { ++ power_adc = 1; ++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, power & 0x7fff); ++ } ++ ++ /* Prepare the codec for AUX reading */ ++ wm->codec->digitiser_ioctl(wm, WM97XX_AUX_PREPARE); ++ ++ /* Turn polling mode on to read AUX ADC */ ++ wm->pen_probably_down = 1; ++ wm->codec->poll_sample(wm, adcsel, &auxval); ++ ++ if (power_adc) ++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, power | 0x8000); ++ ++ wm->codec->digitiser_ioctl(wm, WM97XX_DIG_RESTORE); ++ ++ wm->pen_probably_down = 0; ++ ++ mutex_unlock(&wm->codec_mutex); ++ return auxval & 0xfff; ++} ++ ++#define WM97XX_AUX_ATTR(name,input) \ ++static ssize_t name##_show(struct device *dev, struct device_attribute *attr, char *buf) \ ++{ \ ++ struct wm97xx *wm = (struct wm97xx*)dev->driver_data; \ ++ return sprintf(buf, "%d\n", wm97xx_read_aux_adc(wm, input)); \ ++} \ ++static DEVICE_ATTR(name, 0444, name##_show, NULL) ++ ++WM97XX_AUX_ATTR(aux1, WM97XX_AUX_ID1); ++WM97XX_AUX_ATTR(aux2, WM97XX_AUX_ID2); ++WM97XX_AUX_ATTR(aux3, WM97XX_AUX_ID3); ++WM97XX_AUX_ATTR(aux4, WM97XX_AUX_ID4); ++ ++#define WM97XX_STATUS_ATTR(name) \ ++static ssize_t name##_show(struct device *dev, struct device_attribute *attr, char *buf) \ ++{ \ ++ struct wm97xx *wm = (struct wm97xx*)dev->driver_data; \ ++ return sprintf(buf, "%d\n", wm97xx_reg_read(wm, AC97_GPIO_STATUS)); \ ++} \ ++static DEVICE_ATTR(name, 0444, name##_show, NULL) ++ ++WM97XX_STATUS_ATTR(gpio); ++ ++static int wm97xx_sys_add(struct device *dev) ++{ ++ if (aux_sys) { ++ device_create_file(dev, &dev_attr_aux1); ++ device_create_file(dev, &dev_attr_aux2); ++ device_create_file(dev, &dev_attr_aux3); ++ device_create_file(dev, &dev_attr_aux4); ++ } ++ if (status_sys) ++ device_create_file(dev, &dev_attr_gpio); ++ return 0; ++} ++ ++static void wm97xx_sys_remove(struct device *dev) ++{ ++ if (status_sys) ++ device_remove_file(dev, &dev_attr_gpio); ++ if (aux_sys) { ++ device_remove_file(dev, &dev_attr_aux1); ++ device_remove_file(dev, &dev_attr_aux2); ++ device_remove_file(dev, &dev_attr_aux3); ++ device_remove_file(dev, &dev_attr_aux4); ++ } ++} ++ ++/** ++ * wm97xx_get_gpio - Get the status of a codec GPIO. ++ * @wm: wm97xx device. ++ * @gpio: gpio ++ * ++ * Get the status of a codec GPIO pin ++ */ ++ ++wm97xx_gpio_status_t wm97xx_get_gpio(struct wm97xx *wm, u32 gpio) ++{ ++ u16 status; ++ wm97xx_gpio_status_t ret; ++ ++ mutex_lock(&wm->codec_mutex); ++ status = wm97xx_reg_read(wm, AC97_GPIO_STATUS); ++ ++ if (status & gpio) ++ ret = WM97XX_GPIO_HIGH; ++ else ++ ret = WM97XX_GPIO_LOW; ++ ++ mutex_unlock(&wm->codec_mutex); ++ return ret; ++} ++ ++/** ++ * wm97xx_set_gpio - Set the status of a codec GPIO. ++ * @wm: wm97xx device. ++ * @gpio: gpio ++ * ++ * ++ * Set the status of a codec GPIO pin ++ */ ++ ++void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio, ++ wm97xx_gpio_status_t status) ++{ ++ u16 reg; ++ ++ mutex_lock(&wm->codec_mutex); ++ reg = wm97xx_reg_read(wm, AC97_GPIO_STATUS); ++ ++ if (status & WM97XX_GPIO_HIGH) ++ reg |= gpio; ++ else ++ reg &= ~gpio; ++ ++ if (wm->id == WM9712_ID2) ++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg << 1); ++ else ++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg); ++ mutex_unlock(&wm->codec_mutex); ++} ++ ++/* ++ * Codec GPIO pin configuration, this set's pin direction, polarity, ++ * stickyness and wake up. ++ */ ++void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, wm97xx_gpio_dir_t dir, ++ wm97xx_gpio_pol_t pol, wm97xx_gpio_sticky_t sticky, ++ wm97xx_gpio_wake_t wake) ++{ ++ u16 reg; ++ ++ mutex_lock(&wm->codec_mutex); ++ reg = wm97xx_reg_read(wm, AC97_GPIO_POLARITY); ++ ++ if (pol == WM97XX_GPIO_POL_HIGH) ++ reg |= gpio; ++ else ++ reg &= ~gpio; ++ ++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, reg); ++ reg = wm97xx_reg_read(wm, AC97_GPIO_STICKY); ++ ++ if (sticky == WM97XX_GPIO_STICKY) ++ reg |= gpio; ++ else ++ reg &= ~gpio; ++ ++ wm97xx_reg_write(wm, AC97_GPIO_STICKY, reg); ++ reg = wm97xx_reg_read(wm, AC97_GPIO_WAKEUP); ++ ++ if (wake == WM97XX_GPIO_WAKE) ++ reg |= gpio; ++ else ++ reg &= ~gpio; ++ ++ wm97xx_reg_write(wm, AC97_GPIO_WAKEUP, reg); ++ reg = wm97xx_reg_read(wm, AC97_GPIO_CFG); ++ ++ if (dir == WM97XX_GPIO_IN) ++ reg |= gpio; ++ else ++ reg &= ~gpio; ++ ++ wm97xx_reg_write(wm, AC97_GPIO_CFG, reg); ++ mutex_unlock(&wm->codec_mutex); ++} ++ ++/* ++ * Handle a pen down interrupt. ++ */ ++static void wm97xx_pen_irq_worker(void *ptr) ++{ ++ struct wm97xx *wm = (struct wm97xx *) ptr; ++ ++ /* do we need to enable the touch panel reader */ ++ if (wm->id == WM9705_ID2) { ++ if (wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD) & WM97XX_PEN_DOWN) ++ wm->pen_is_down = 1; ++ else ++ wm->pen_is_down = 0; ++ wake_up_interruptible(&wm->pen_irq_wait); ++ } else { ++ u16 status, pol; ++ mutex_lock(&wm->codec_mutex); ++ status = wm97xx_reg_read(wm, AC97_GPIO_STATUS); ++ pol = wm97xx_reg_read(wm, AC97_GPIO_POLARITY); ++ ++ if (WM97XX_GPIO_13 & pol & status) { ++ wm->pen_is_down = 1; ++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, pol & ~WM97XX_GPIO_13); ++ } else { ++ wm->pen_is_down = 0; ++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, pol | WM97XX_GPIO_13); ++ } ++ ++ if (wm->id == WM9712_ID2) ++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, (status & ~WM97XX_GPIO_13) << 1); ++ else ++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, status & ~WM97XX_GPIO_13); ++ mutex_unlock(&wm->codec_mutex); ++ wake_up_interruptible(&wm->pen_irq_wait); ++ } ++ ++ if (!wm->pen_is_down && wm->mach_ops && wm->mach_ops->acc_enabled) ++ wm->mach_ops->acc_pen_up(wm); ++ enable_irq(wm->pen_irq); ++} ++ ++/* ++ * Codec PENDOWN irq handler ++ * ++ * We have to disable the codec interrupt in the handler because it can ++ * take upto 1ms to clear the interrupt source. The interrupt is then enabled ++ * again in the slow handler when the source has been cleared. ++ */ ++static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id, ++ struct pt_regs *regs) ++{ ++ struct wm97xx *wm = (struct wm97xx *) dev_id; ++ disable_irq(wm->pen_irq); ++ queue_work(wm->pen_irq_workq, &wm->pen_event_work); ++ return IRQ_HANDLED; ++} ++ ++/* ++ * initialise pen IRQ handler and workqueue ++ */ ++static int wm97xx_init_pen_irq(struct wm97xx *wm) ++{ ++ u16 reg; ++ ++ INIT_WORK(&wm->pen_event_work, wm97xx_pen_irq_worker, wm); ++ if ((wm->pen_irq_workq = ++ create_singlethread_workqueue("kwm97pen")) == NULL) { ++ err("could not create pen irq work queue"); ++ wm->pen_irq = 0; ++ return -EINVAL; ++ } ++ ++ if (request_irq (wm->pen_irq, wm97xx_pen_interrupt, SA_SHIRQ, "wm97xx-pen", wm)) { ++ err("could not register codec pen down interrupt, will poll for pen down"); ++ destroy_workqueue(wm->pen_irq_workq); ++ wm->pen_irq = 0; ++ return -EINVAL; ++ } ++ ++ /* enable PEN down on wm9712/13 */ ++ if (wm->id != WM9705_ID2) { ++ reg = wm97xx_reg_read(wm, AC97_MISC_AFE); ++ wm97xx_reg_write(wm, AC97_MISC_AFE, reg & 0xfffb); ++ reg = wm97xx_reg_read(wm, 0x5a); ++ wm97xx_reg_write(wm, 0x5a, reg & ~0x0001); ++ } ++ ++ return 0; ++} ++ ++/* Private struct for communication between struct wm97xx_tshread ++ * and wm97xx_read_samples */ ++struct ts_state { ++ int sleep_time; ++ int min_sleep_time; ++}; ++ ++static int wm97xx_read_samples(struct wm97xx *wm, struct ts_state *state) ++{ ++ struct wm97xx_data data; ++ int rc; ++ ++ mutex_lock(&wm->codec_mutex); ++ ++ if (wm->mach_ops && wm->mach_ops->acc_enabled) ++ rc = wm->mach_ops->acc_pen_down(wm); ++ else ++ rc = wm->codec->poll_touch(wm, &data); ++ ++ if (rc & RC_PENUP) { ++ if (wm->pen_is_down) { ++ wm->pen_is_down = 0; ++ dbg("pen up"); ++ input_report_abs(wm->input_dev, ABS_PRESSURE, 0); ++ input_sync(wm->input_dev); ++ } else if (!(rc & RC_AGAIN)) { ++ /* We need high frequency updates only while pen is down, ++ * the user never will be able to touch screen faster than ++ * a few times per second... On the other hand, when the ++ * user is actively working with the touchscreen we don't ++ * want to lose the quick response. So we will slowly ++ * increase sleep time after the pen is up and quicky ++ * restore it to ~one task switch when pen is down again. ++ */ ++ if (state->sleep_time < HZ / 10) ++ state->sleep_time++; ++ } ++ ++ } else if (rc & RC_VALID) { ++ dbg("pen down: x=%x:%d, y=%x:%d, pressure=%x:%d\n", ++ data.x >> 12, data.x & 0xfff, data.y >> 12, ++ data.y & 0xfff, data.p >> 12, data.p & 0xfff); ++ input_report_abs(wm->input_dev, ABS_X, data.x & 0xfff); ++ input_report_abs(wm->input_dev, ABS_Y, data.y & 0xfff); ++ input_report_abs(wm->input_dev, ABS_PRESSURE, data.p & 0xfff); ++ input_sync(wm->input_dev); ++ wm->pen_is_down = 1; ++ state->sleep_time = state->min_sleep_time; ++ } else if (rc & RC_PENDOWN) { ++ dbg("pen down"); ++ wm->pen_is_down = 1; ++ state->sleep_time = state->min_sleep_time; ++ } ++ ++ mutex_unlock(&wm->codec_mutex); ++ return rc; ++} ++ ++/* ++* The touchscreen sample reader thread. ++*/ ++static int wm97xx_ts_read(void *data) ++{ ++ int rc; ++ struct ts_state state; ++ struct wm97xx *wm = (struct wm97xx *) data; ++ ++ /* set up thread context */ ++ wm->ts_task = current; ++ daemonize("kwm97xxts"); ++ ++ if (wm->codec == NULL) { ++ wm->ts_task = NULL; ++ printk(KERN_ERR "codec is NULL, bailing\n"); ++ } ++ ++ complete(&wm->ts_init); ++ wm->pen_is_down = 0; ++ state.min_sleep_time = HZ >= 100 ? HZ / 100 : 1; ++ if (state.min_sleep_time < 1) ++ state.min_sleep_time = 1; ++ state.sleep_time = state.min_sleep_time; ++ ++ /* touch reader loop */ ++ while (wm->ts_task) { ++ do { ++ try_to_freeze(); ++ rc = wm97xx_read_samples(wm, &state); ++ } while (rc & RC_AGAIN); ++ if (!wm->pen_is_down && wm->pen_irq) { ++ /* Nice, we don't have to poll for pen down event */ ++ wait_event_interruptible(wm->pen_irq_wait, wm->pen_is_down); ++ } else { ++ set_task_state(current, TASK_INTERRUPTIBLE); ++ schedule_timeout(state.sleep_time); ++ } ++ } ++ complete_and_exit(&wm->ts_exit, 0); ++} ++ ++/** ++ * wm97xx_ts_input_open - Open the touch screen input device. ++ * @idev: Input device to be opened. ++ * ++ * Called by the input sub system to open a wm97xx touchscreen device. ++ * Starts the touchscreen thread and touch digitiser. ++ */ ++static int wm97xx_ts_input_open(struct input_dev *idev) ++{ ++ int ret = 0; ++ struct wm97xx *wm = (struct wm97xx *) idev->private; ++ ++ mutex_lock(&wm->codec_mutex); ++ /* first time opened ? */ ++ if (wm->ts_use_count++ == 0) { ++ /* start touchscreen thread */ ++ init_completion(&wm->ts_init); ++ init_completion(&wm->ts_exit); ++ ret = kernel_thread(wm97xx_ts_read, wm, CLONE_KERNEL); ++ ++ if (ret >= 0) { ++ wait_for_completion(&wm->ts_init); ++ if (wm->ts_task == NULL) ++ ret = -EINVAL; ++ } else { ++ mutex_unlock(&wm->codec_mutex); ++ return ret; ++ } ++ ++ /* start digitiser */ ++ if (wm->mach_ops && wm->mach_ops->acc_enabled) ++ wm->codec->acc_enable(wm, 1); ++ wm->codec->digitiser_ioctl(wm, WM97XX_DIG_START); ++ ++ /* init pen down/up irq handling */ ++ if (wm->pen_irq) { ++ wm97xx_init_pen_irq(wm); ++ ++ if (wm->pen_irq == 0) { ++ /* we failed to get an irq for pen down events, ++ * so we resort to polling. kickstart the reader */ ++ wm->pen_is_down = 1; ++ wake_up_interruptible(&wm->pen_irq_wait); ++ } ++ } ++ } ++ ++ mutex_unlock(&wm->codec_mutex); ++ return 0; ++} ++ ++/** ++ * wm97xx_ts_input_close - Close the touch screen input device. ++ * @idev: Input device to be closed. ++ * ++ * Called by the input sub system to close a wm97xx touchscreen device. ++ * Kills the touchscreen thread and stops the touch digitiser. ++ */ ++ ++static void wm97xx_ts_input_close(struct input_dev *idev) ++{ ++ struct wm97xx *wm = (struct wm97xx *) idev->private; ++ ++ mutex_lock(&wm->codec_mutex); ++ if (--wm->ts_use_count == 0) { ++ /* destroy workqueues and free irqs */ ++ if (wm->pen_irq) { ++ free_irq(wm->pen_irq, wm); ++ destroy_workqueue(wm->pen_irq_workq); ++ } ++ ++ /* kill thread */ ++ if (wm->ts_task) { ++ wm->ts_task = NULL; ++ wm->pen_is_down = 1; ++ wake_up_interruptible(&wm->pen_irq_wait); ++ wait_for_completion(&wm->ts_exit); ++ wm->pen_is_down = 0; ++ } ++ ++ /* stop digitiser */ ++ wm->codec->digitiser_ioctl(wm, WM97XX_DIG_STOP); ++ if (wm->mach_ops && wm->mach_ops->acc_enabled) ++ wm->codec->acc_enable(wm, 0); ++ } ++ mutex_unlock(&wm->codec_mutex); ++} ++ ++static int wm97xx_bus_match(struct device *dev, struct device_driver *drv) ++{ ++ return !(strcmp(dev->bus_id,drv->name)); ++} ++ ++/* ++ * The AC97 audio driver will do all the Codec suspend and resume ++ * tasks. This is just for anything machine specific or extra. ++ */ ++static int wm97xx_bus_suspend(struct device *dev, pm_message_t state) ++{ ++ int ret = 0; ++ ++ if (dev->driver && dev->driver->suspend) ++ ret = dev->driver->suspend(dev, state); ++ ++ return ret; ++} ++ ++static int wm97xx_bus_resume(struct device *dev) ++{ ++ int ret = 0; ++ ++ if (dev->driver && dev->driver->resume) ++ ret = dev->driver->resume(dev); ++ ++ return ret; ++} ++ ++struct bus_type wm97xx_bus_type = { ++ .name = "wm97xx", ++ .match = wm97xx_bus_match, ++ .suspend = wm97xx_bus_suspend, ++ .resume = wm97xx_bus_resume, ++}; ++ ++static void wm97xx_release(struct device *dev) ++{ ++ kfree(dev); ++} ++ ++static int wm97xx_probe(struct device *dev) ++{ ++ struct wm97xx* wm; ++ int ret = 0, id = 0; ++ ++ if (!(wm = kzalloc(sizeof(struct wm97xx), GFP_KERNEL))) ++ return -ENOMEM; ++ mutex_init(&wm->codec_mutex); ++ ++ init_waitqueue_head(&wm->pen_irq_wait); ++ wm->dev = dev; ++ dev->driver_data = wm; ++ wm->ac97 = to_ac97_t(dev); ++ ++ /* check that we have a supported codec */ ++ if ((id = wm97xx_reg_read(wm, AC97_VENDOR_ID1)) != WM97XX_ID1) { ++ err("could not find a wm97xx, found a %x instead\n", id); ++ kfree(wm); ++ return -ENODEV; ++ } ++ ++ wm->id = wm97xx_reg_read(wm, AC97_VENDOR_ID2); ++ if(wm->id != wm97xx_codec.id) { ++ err("could not find a the selected codec, please build for wm97%2x", wm->id & 0xff); ++ kfree(wm); ++ return -ENODEV; ++ } ++ ++ if((wm->input_dev = input_allocate_device()) == NULL) { ++ kfree(wm); ++ return -ENOMEM; ++ } ++ ++ /* set up touch configuration */ ++ info("detected a wm97%2x codec", wm->id & 0xff); ++ wm->input_dev->name = "wm97xx touchscreen"; ++ wm->input_dev->open = wm97xx_ts_input_open; ++ wm->input_dev->close = wm97xx_ts_input_close; ++ set_bit(EV_ABS, wm->input_dev->evbit); ++ set_bit(ABS_X, wm->input_dev->absbit); ++ set_bit(ABS_Y, wm->input_dev->absbit); ++ set_bit(ABS_PRESSURE, wm->input_dev->absbit); ++ wm->input_dev->absmax[ABS_X] = abs_x[1]; ++ wm->input_dev->absmax[ABS_Y] = abs_y[1]; ++ wm->input_dev->absmax[ABS_PRESSURE] = abs_p[1]; ++ wm->input_dev->absmin[ABS_X] = abs_x[0]; ++ wm->input_dev->absmin[ABS_Y] = abs_y[0]; ++ wm->input_dev->absmin[ABS_PRESSURE] = abs_p[0]; ++ wm->input_dev->absfuzz[ABS_X] = abs_x[2]; ++ wm->input_dev->absfuzz[ABS_Y] = abs_y[2]; ++ wm->input_dev->absfuzz[ABS_PRESSURE] = abs_p[2]; ++ wm->input_dev->private = wm; ++ wm->codec = &wm97xx_codec; ++ if((ret = input_register_device(wm->input_dev)) < 0) { ++ kfree(wm); ++ return -ENOMEM; ++ } ++ ++ if(aux_sys) ++ wm97xx_sys_add(dev); ++ ++ /* set up physical characteristics */ ++ wm->codec->digitiser_ioctl(wm, WM97XX_PHY_INIT); ++ ++ /* load gpio cache */ ++ wm->gpio[0] = wm97xx_reg_read(wm, AC97_GPIO_CFG); ++ wm->gpio[1] = wm97xx_reg_read(wm, AC97_GPIO_POLARITY); ++ wm->gpio[2] = wm97xx_reg_read(wm, AC97_GPIO_STICKY); ++ wm->gpio[3] = wm97xx_reg_read(wm, AC97_GPIO_WAKEUP); ++ wm->gpio[4] = wm97xx_reg_read(wm, AC97_GPIO_STATUS); ++ wm->gpio[5] = wm97xx_reg_read(wm, AC97_MISC_AFE); ++ ++ /* register our battery device */ ++ if (!(wm->battery_dev = kzalloc(sizeof(struct device), GFP_KERNEL))) { ++ ret = -ENOMEM; ++ goto batt_err; ++ } ++ wm->battery_dev->bus = &wm97xx_bus_type; ++ strcpy(wm->battery_dev->bus_id,"wm97xx-battery"); ++ wm->battery_dev->driver_data = wm; ++ wm->battery_dev->parent = dev; ++ wm->battery_dev->release = wm97xx_release; ++ if((ret = device_register(wm->battery_dev)) < 0) ++ goto batt_reg_err; ++ ++ /* register our extended touch device (for machine specific extensions) */ ++ if (!(wm->touch_dev = kzalloc(sizeof(struct device), GFP_KERNEL))) { ++ ret = -ENOMEM; ++ goto touch_err; ++ } ++ wm->touch_dev->bus = &wm97xx_bus_type; ++ strcpy(wm->touch_dev->bus_id,"wm97xx-touchscreen"); ++ wm->touch_dev->driver_data = wm; ++ wm->touch_dev->parent = dev; ++ wm->touch_dev->release = wm97xx_release; ++ if((ret = device_register(wm->touch_dev)) < 0) ++ goto touch_reg_err; ++ ++ return ret; ++ ++touch_reg_err: ++ kfree(wm->touch_dev); ++touch_err: ++ device_unregister(wm->battery_dev); ++batt_reg_err: ++ kfree(wm->battery_dev); ++batt_err: ++ input_unregister_device(wm->input_dev); ++ kfree(wm); ++ return ret; ++} ++ ++static int wm97xx_remove(struct device *dev) ++{ ++ struct wm97xx *wm = dev_get_drvdata(dev); ++ ++ /* Stop touch reader thread */ ++ if (wm->ts_task) { ++ wm->ts_task = NULL; ++ wm->pen_is_down = 1; ++ wake_up_interruptible(&wm->pen_irq_wait); ++ wait_for_completion(&wm->ts_exit); ++ } ++ device_unregister(wm->battery_dev); ++ device_unregister(wm->touch_dev); ++ input_unregister_device(wm->input_dev); ++ ++ if(aux_sys) ++ wm97xx_sys_remove(dev); ++ ++ kfree(wm); ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++int wm97xx_resume(struct device* dev) ++{ ++ struct wm97xx *wm = dev_get_drvdata(dev); ++ ++ /* restore digitiser and gpio's */ ++ if(wm->id == WM9713_ID2) { ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, wm->dig[0]); ++ wm97xx_reg_write(wm, 0x5a, wm->misc); ++ if(wm->ts_use_count) { ++ u16 reg = wm97xx_reg_read(wm, AC97_EXTENDED_MID) & 0x7fff; ++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, reg); ++ } ++ } ++ ++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, wm->dig[1]); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2]); ++ ++ wm97xx_reg_write(wm, AC97_GPIO_CFG, wm->gpio[0]); ++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, wm->gpio[1]); ++ wm97xx_reg_write(wm, AC97_GPIO_STICKY, wm->gpio[2]); ++ wm97xx_reg_write(wm, AC97_GPIO_WAKEUP, wm->gpio[3]); ++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, wm->gpio[4]); ++ wm97xx_reg_write(wm, AC97_MISC_AFE, wm->gpio[5]); ++ ++ return 0; ++} ++ ++#else ++#define wm97xx_resume NULL ++#endif ++ ++int wm97xx_register_mach_ops(struct wm97xx *wm, struct wm97xx_mach_ops *mach_ops) ++{ ++ mutex_lock(&wm->codec_mutex); ++ if(wm->mach_ops) { ++ mutex_unlock(&wm->codec_mutex); ++ return -EINVAL; ++ } ++ wm->mach_ops = mach_ops; ++ mutex_unlock(&wm->codec_mutex); ++ return 0; ++} ++ ++void wm97xx_unregister_mach_ops(struct wm97xx *wm) ++{ ++ mutex_lock(&wm->codec_mutex); ++ wm->mach_ops = NULL; ++ mutex_unlock(&wm->codec_mutex); ++} ++ ++static struct device_driver wm97xx_driver = { ++ .name = "ac97", ++ .bus = &ac97_bus_type, ++ .owner = THIS_MODULE, ++ .probe = wm97xx_probe, ++ .remove = wm97xx_remove, ++ .resume = wm97xx_resume, ++}; ++ ++static int __init wm97xx_init(void) ++{ ++ int ret; ++ ++ info("version %s liam.girdwood@wolfsonmicro.com", WM_CORE_VERSION); ++ if((ret = bus_register(&wm97xx_bus_type)) < 0) ++ return ret; ++ return driver_register(&wm97xx_driver); ++} ++ ++static void __exit wm97xx_exit(void) ++{ ++ driver_unregister(&wm97xx_driver); ++ bus_unregister(&wm97xx_bus_type); ++} ++ ++EXPORT_SYMBOL_GPL(wm97xx_get_gpio); ++EXPORT_SYMBOL_GPL(wm97xx_set_gpio); ++EXPORT_SYMBOL_GPL(wm97xx_config_gpio); ++EXPORT_SYMBOL_GPL(wm97xx_read_aux_adc); ++EXPORT_SYMBOL_GPL(wm97xx_reg_read); ++EXPORT_SYMBOL_GPL(wm97xx_reg_write); ++EXPORT_SYMBOL_GPL(wm97xx_bus_type); ++EXPORT_SYMBOL_GPL(wm97xx_register_mach_ops); ++EXPORT_SYMBOL_GPL(wm97xx_unregister_mach_ops); ++ ++module_init(wm97xx_init); ++module_exit(wm97xx_exit); ++ ++/* Module information */ ++MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); ++MODULE_DESCRIPTION("WM97xx Core - Touch Screen / AUX ADC / GPIO Driver"); ++MODULE_LICENSE("GPL"); +Index: linux-2.6.17/include/linux/wm97xx.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.17/include/linux/wm97xx.h 2006-09-19 20:36:47.973052250 +0200 +@@ -0,0 +1,291 @@ ++ ++/* ++ * Register bits and API for Wolfson WM97xx series of codecs ++ */ ++ ++#ifndef _LINUX_WM97XX_H ++#define _LINUX_WM97XX_H ++ ++#include <sound/driver.h> ++#include <sound/core.h> ++#include <sound/pcm.h> ++#include <sound/ac97_codec.h> ++#include <sound/initval.h> ++#include <linux/types.h> ++#include <linux/list.h> ++#include <linux/input.h> /* Input device layer */ ++ ++/* ++ * WM97xx AC97 Touchscreen registers ++ */ ++#define AC97_WM97XX_DIGITISER1 0x76 ++#define AC97_WM97XX_DIGITISER2 0x78 ++#define AC97_WM97XX_DIGITISER_RD 0x7a ++#define AC97_WM9713_DIG1 0x74 ++#define AC97_WM9713_DIG2 AC97_WM97XX_DIGITISER1 ++#define AC97_WM9713_DIG3 AC97_WM97XX_DIGITISER2 ++ ++/* ++ * WM97xx register bits ++ */ ++#define WM97XX_POLL 0x8000 /* initiate a polling measurement */ ++#define WM97XX_ADCSEL_X 0x1000 /* x coord measurement */ ++#define WM97XX_ADCSEL_Y 0x2000 /* y coord measurement */ ++#define WM97XX_ADCSEL_PRES 0x3000 /* pressure measurement */ ++#define WM97XX_ADCSEL_MASK 0x7000 ++#define WM97XX_COO 0x0800 /* enable coordinate mode */ ++#define WM97XX_CTC 0x0400 /* enable continuous mode */ ++#define WM97XX_CM_RATE_93 0x0000 /* 93.75Hz continuous rate */ ++#define WM97XX_CM_RATE_187 0x0100 /* 187.5Hz continuous rate */ ++#define WM97XX_CM_RATE_375 0x0200 /* 375Hz continuous rate */ ++#define WM97XX_CM_RATE_750 0x0300 /* 750Hz continuous rate */ ++#define WM97XX_CM_RATE_8K 0x00f0 /* 8kHz continuous rate */ ++#define WM97XX_CM_RATE_12K 0x01f0 /* 12kHz continuous rate */ ++#define WM97XX_CM_RATE_24K 0x02f0 /* 24kHz continuous rate */ ++#define WM97XX_CM_RATE_48K 0x03f0 /* 48kHz continuous rate */ ++#define WM97XX_CM_RATE_MASK 0x03f0 ++#define WM97XX_RATE(i) (((i & 3) << 8) | ((i & 4) ? 0xf0 : 0)) ++#define WM97XX_DELAY(i) ((i << 4) & 0x00f0) /* sample delay times */ ++#define WM97XX_DELAY_MASK 0x00f0 ++#define WM97XX_SLEN 0x0008 /* slot read back enable */ ++#define WM97XX_SLT(i) ((i - 5) & 0x7) /* touchpanel slot selection (5-11) */ ++#define WM97XX_SLT_MASK 0x0007 ++#define WM97XX_PRP_DETW 0x4000 /* pen detect on, digitiser off, wake up */ ++#define WM97XX_PRP_DET 0x8000 /* pen detect on, digitiser off, no wake up */ ++#define WM97XX_PRP_DET_DIG 0xc000 /* pen detect on, digitiser on */ ++#define WM97XX_RPR 0x2000 /* wake up on pen down */ ++#define WM97XX_PEN_DOWN 0x8000 /* pen is down */ ++#define WM97XX_ADCSRC_MASK 0x7000 /* ADC source mask */ ++ ++#define WM97XX_AUX_ID1 0x8001 ++#define WM97XX_AUX_ID2 0x8002 ++#define WM97XX_AUX_ID3 0x8003 ++#define WM97XX_AUX_ID4 0x8004 ++ ++ ++/* WM9712 Bits */ ++#define WM9712_45W 0x1000 /* set for 5-wire touchscreen */ ++#define WM9712_PDEN 0x0800 /* measure only when pen down */ ++#define WM9712_WAIT 0x0200 /* wait until adc is read before next sample */ ++#define WM9712_PIL 0x0100 /* current used for pressure measurement. set 400uA else 200uA */ ++#define WM9712_MASK_HI 0x0040 /* hi on mask pin (47) stops conversions */ ++#define WM9712_MASK_EDGE 0x0080 /* rising/falling edge on pin delays sample */ ++#define WM9712_MASK_SYNC 0x00c0 /* rising/falling edge on mask initiates sample */ ++#define WM9712_RPU(i) (i&0x3f) /* internal pull up on pen detect (64k / rpu) */ ++#define WM9712_PD(i) (0x1 << i) /* power management */ ++ ++/* WM9712 Registers */ ++#define AC97_WM9712_POWER 0x24 ++#define AC97_WM9712_REV 0x58 ++ ++/* WM9705 Bits */ ++#define WM9705_PDEN 0x1000 /* measure only when pen is down */ ++#define WM9705_PINV 0x0800 /* inverts sense of pen down output */ ++#define WM9705_BSEN 0x0400 /* BUSY flag enable, pin47 is 1 when busy */ ++#define WM9705_BINV 0x0200 /* invert BUSY (pin47) output */ ++#define WM9705_WAIT 0x0100 /* wait until adc is read before next sample */ ++#define WM9705_PIL 0x0080 /* current used for pressure measurement. set 400uA else 200uA */ ++#define WM9705_PHIZ 0x0040 /* set PHONE and PCBEEP inputs to high impedance */ ++#define WM9705_MASK_HI 0x0010 /* hi on mask stops conversions */ ++#define WM9705_MASK_EDGE 0x0020 /* rising/falling edge on pin delays sample */ ++#define WM9705_MASK_SYNC 0x0030 /* rising/falling edge on mask initiates sample */ ++#define WM9705_PDD(i) (i & 0x000f) /* pen detect comparator threshold */ ++ ++ ++/* WM9713 Bits */ ++#define WM9713_PDPOL 0x0400 /* Pen down polarity */ ++#define WM9713_POLL 0x0200 /* initiate a polling measurement */ ++#define WM9713_CTC 0x0100 /* enable continuous mode */ ++#define WM9713_ADCSEL_X 0x0002 /* X measurement */ ++#define WM9713_ADCSEL_Y 0x0004 /* Y measurement */ ++#define WM9713_ADCSEL_PRES 0x0008 /* Pressure measurement */ ++#define WM9713_COO 0x0001 /* enable coordinate mode */ ++#define WM9713_PDEN 0x0800 /* measure only when pen down */ ++#define WM9713_ADCSEL_MASK 0x00fe /* ADC selection mask */ ++#define WM9713_WAIT 0x0200 /* coordinate wait */ ++ ++/* AUX ADC ID's */ ++#define TS_COMP1 0x0 ++#define TS_COMP2 0x1 ++#define TS_BMON 0x2 ++#define TS_WIPER 0x3 ++ ++/* ID numbers */ ++#define WM97XX_ID1 0x574d ++#define WM9712_ID2 0x4c12 ++#define WM9705_ID2 0x4c05 ++#define WM9713_ID2 0x4c13 ++ ++/* Codec GPIO's */ ++#define WM97XX_MAX_GPIO 16 ++#define WM97XX_GPIO_1 (1 << 1) ++#define WM97XX_GPIO_2 (1 << 2) ++#define WM97XX_GPIO_3 (1 << 3) ++#define WM97XX_GPIO_4 (1 << 4) ++#define WM97XX_GPIO_5 (1 << 5) ++#define WM97XX_GPIO_6 (1 << 6) ++#define WM97XX_GPIO_7 (1 << 7) ++#define WM97XX_GPIO_8 (1 << 8) ++#define WM97XX_GPIO_9 (1 << 9) ++#define WM97XX_GPIO_10 (1 << 10) ++#define WM97XX_GPIO_11 (1 << 11) ++#define WM97XX_GPIO_12 (1 << 12) ++#define WM97XX_GPIO_13 (1 << 13) ++#define WM97XX_GPIO_14 (1 << 14) ++#define WM97XX_GPIO_15 (1 << 15) ++ ++ ++#define AC97_LINK_FRAME 21 /* time in uS for AC97 link frame */ ++ ++ ++/*---------------- Return codes from sample reading functions ---------------*/ ++ ++/* More data is available; call the sample gathering function again */ ++#define RC_AGAIN 0x00000001 ++/* The returned sample is valid */ ++#define RC_VALID 0x00000002 ++/* The pen is up (the first RC_VALID without RC_PENUP means pen is down) */ ++#define RC_PENUP 0x00000004 ++/* The pen is down (RC_VALID implies RC_PENDOWN, but sometimes it is helpful ++ to tell the handler that the pen is down but we don't know yet his coords, ++ so the handler should not sleep or wait for pendown irq) */ ++#define RC_PENDOWN 0x00000008 ++ ++/* The wm97xx driver provides a private API for writing platform-specific ++ * drivers. ++ */ ++ ++/* The structure used to return arch specific sampled data into */ ++struct wm97xx_data { ++ int x; ++ int y; ++ int p; ++}; ++ ++/* Codec GPIO status ++ */ ++typedef enum { ++ WM97XX_GPIO_HIGH, ++ WM97XX_GPIO_LOW ++} wm97xx_gpio_status_t; ++ ++/* Codec GPIO direction ++ */ ++typedef enum { ++ WM97XX_GPIO_IN, ++ WM97XX_GPIO_OUT ++} wm97xx_gpio_dir_t; ++ ++/* Codec GPIO polarity ++ */ ++typedef enum { ++ WM97XX_GPIO_POL_HIGH, ++ WM97XX_GPIO_POL_LOW ++} wm97xx_gpio_pol_t; ++ ++/* Codec GPIO sticky ++ */ ++typedef enum { ++ WM97XX_GPIO_STICKY, ++ WM97XX_GPIO_NOTSTICKY ++} wm97xx_gpio_sticky_t; ++ ++/* Codec GPIO wake ++ */ ++typedef enum { ++ WM97XX_GPIO_WAKE, ++ WM97XX_GPIO_NOWAKE ++} wm97xx_gpio_wake_t; ++ ++ ++/* ++ * Digitiser ioctl commands ++ */ ++#define WM97XX_DIG_START 0x1 ++#define WM97XX_DIG_STOP 0x2 ++#define WM97XX_PHY_INIT 0x3 ++#define WM97XX_AUX_PREPARE 0x4 ++#define WM97XX_DIG_RESTORE 0x5 ++ ++struct wm97xx; ++extern struct wm97xx_codec_drv wm97xx_codec; ++ ++/* ++ * Codec driver interface - allows mapping to WM9705/12/13 and newer codecs ++ */ ++struct wm97xx_codec_drv { ++ u16 id; ++ char *name; ++ int (*poll_sample) (struct wm97xx *, int adcsel, int *sample); /* read 1 sample */ ++ int (*poll_touch) (struct wm97xx *, struct wm97xx_data *); /* read X,Y,[P] in poll */ ++ int (*digitiser_ioctl) (struct wm97xx *, int cmd); ++ int (*acc_enable) (struct wm97xx *, int enable); ++}; ++ ++ ++/* Machine specific and accelerated touch operations */ ++struct wm97xx_mach_ops { ++ ++ /* accelerated touch readback - coords are transmited on AC97 link */ ++ int acc_enabled; ++ void (*acc_pen_up) (struct wm97xx *); ++ int (*acc_pen_down) (struct wm97xx *); ++ int (*acc_startup) (struct wm97xx *); ++ void (*acc_shutdown) (struct wm97xx *); ++ ++ /* pre and post sample - can be used to minimise any analog noise */ ++ void (*pre_sample) (int); /* function to run before sampling */ ++ void (*post_sample) (int); /* function to run after sampling */ ++}; ++ ++struct wm97xx { ++ u16 dig[3], id, gpio[6], misc; /* Cached codec registers */ ++ u16 dig_save[3]; /* saved during aux reading */ ++ struct wm97xx_codec_drv *codec; /* attached codec driver*/ ++ struct input_dev* input_dev; /* touchscreen input device */ ++ ac97_t *ac97; /* ALSA codec access */ ++ struct device *dev; /* ALSA device */ ++ struct device *battery_dev; ++ struct device *touch_dev; ++ struct wm97xx_mach_ops *mach_ops; ++ struct mutex codec_mutex; ++ struct completion ts_init; ++ struct completion ts_exit; ++ struct task_struct *ts_task; ++ unsigned int pen_irq; /* Pen IRQ number in use */ ++ wait_queue_head_t pen_irq_wait; /* Pen IRQ wait queue */ ++ struct workqueue_struct *pen_irq_workq; ++ struct work_struct pen_event_work; ++ u16 acc_slot; /* AC97 slot used for acc touch data */ ++ u16 acc_rate; /* acc touch data rate */ ++ unsigned int ts_use_count; ++ unsigned pen_is_down:1; /* Pen is down */ ++ unsigned aux_waiting:1; /* aux measurement waiting */ ++ unsigned pen_probably_down:1; /* used in polling mode */ ++}; ++ ++/* Codec GPIO access (not supported on WM9705) ++ * This can be used to set/get codec GPIO and Virtual GPIO status. ++ */ ++wm97xx_gpio_status_t wm97xx_get_gpio(struct wm97xx *wm, u32 gpio); ++void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio, ++ wm97xx_gpio_status_t status); ++void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, ++ wm97xx_gpio_dir_t dir, ++ wm97xx_gpio_pol_t pol, ++ wm97xx_gpio_sticky_t sticky, ++ wm97xx_gpio_wake_t wake); ++ ++/* codec AC97 IO access */ ++int wm97xx_reg_read(struct wm97xx *wm, u16 reg); ++void wm97xx_reg_write(struct wm97xx *wm, u16 reg, u16 val); ++ ++/* aux adc readback */ ++int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel); ++ ++/* machine ops */ ++int wm97xx_register_mach_ops(struct wm97xx *, struct wm97xx_mach_ops *); ++void wm97xx_unregister_mach_ops(struct wm97xx *); ++ ++extern struct bus_type wm97xx_bus_type; ++#endif diff --git a/meta/packages/linux/linux-rp_2.6.23+2.6.24-rc8.bb b/meta/packages/linux/linux-rp_2.6.23+2.6.24-rc8.bb index 353e8c1fc3..efffb6e404 100644 --- a/meta/packages/linux/linux-rp_2.6.23+2.6.24-rc8.bb +++ b/meta/packages/linux/linux-rp_2.6.23+2.6.24-rc8.bb @@ -1,6 +1,6 @@ require linux-rp.inc -PR = "r2" +PR = "r4" DEFAULT_PREFERENCE = "-1" @@ -17,43 +17,44 @@ DEFAULT_PREFERENCE = "-1" # Hacks should clearly named and at the bottom SRC_URI = "${KERNELORG_MIRROR}pub/linux/kernel/v2.6/linux-2.6.23.tar.bz2 \ ${KERNELORG_MIRROR}pub/linux/kernel/v2.6/testing/patch-2.6.24-rc8.bz2;patch=1 \ - ${RPSRC}/lzo_crypto-r2.patch;patch=1 \ + ${RPSRC}/export_atags-r2.patch;patch=1;status=pending \ + ${RPSRC}/lzo_crypto-r2.patch;patch=1;status=pending \ + ${RPSRC}/corgi_rearrange_lcd-r0.patch;patch=1;status=pending \ ${RPSRC}/lzo_jffs2_sysfs-r1.patch;patch=1 \ - file://hx2750_base-r31.patch;patch=1 \ + ${RPSRC}/hx2750_base-r31.patch;patch=1 \ ${RPSRC}/hx2750_bl-r9.patch;patch=1 \ ${RPSRC}/hx2750_pcmcia-r3.patch;patch=1 \ ${RPSRC}/pxa_keys-r8.patch;patch=1 \ -# ${RPSRC}/tsc2101-r16.patch;patch=1 \ + ${RPSRC}/tsc2101-r17.patch;patch=1 \ ${RPSRC}/hx2750_test1-r7.patch;patch=1 \ ${RPSRC}/input_power-r10.patch;patch=1 \ - ${RPSRC}/input_power_fix-r0.patch;patch=1 \ ${RPSRC}/pxa25x_cpufreq-r2.patch;patch=1 \ ${RPSRC}/sharpsl_pm_fixes1-r0.patch;patch=1 \ ${RPSRC}/pm_changes-r1.patch;patch=1 \ ${RPSRC}/usb_add_epalloc-r4.patch;patch=1 \ - ${RPSRC}/usb_pxa27x_udc-r7.patch;patch=1 \ + ${RPSRC}/usb_pxa27x_udc-r8.patch;patch=1 \ ${RPSRC}/locomo_kbd_tweak-r1.patch;patch=1 \ - ${RPSRC}/poodle_pm-r5.patch;patch=1 \ - file://pxa27x_overlay-r8.patch;patch=1 \ - ${RPSRC}/w100_extaccel-r1.patch;patch=1 \ + ${RPSRC}/pxa27x_overlay-r8.patch;patch=1 \ + ${RPSRC}/w100_extaccel-r2.patch;patch=1 \ ${RPSRC}/w100_extmem-r1.patch;patch=1 \ - ${RPSRC}/export_atags-r1.patch;patch=1 \ - file://w100fb-unused-var.patch;patch=1 \ - file://hostap-monitor-mode.patch;patch=1 \ - file://serial-add-support-for-non-standard-xtals-to-16c950-driver.patch;patch=1 \ + ${RPSRC}/poodle_pm-r5.patch;patch=1 \ + ${RPSRC}/poodle_lcd_hack-r0.patch;patch=1 \ + ${RPSRC}/poodle_asoc_fix-r1.patch;patch=1 \ + file://squashfs3.3.patch;patch=1;status=external \ ${RPSRC}/logo_oh-r1.patch.bz2;patch=1;status=unmergable \ ${RPSRC}/pxa-linking-bug.patch;patch=1;status=unmergable \ + file://hostap-monitor-mode.patch;patch=1;status=unmergable \ + file://serial-add-support-for-non-standard-xtals-to-16c950-driver.patch;patch=1;status=unmergable \ ${RPSRC}/mmcsd_large_cards-r1.patch;patch=1;status=hack \ - file://mmcsd_no_scr_check-r2.patch;patch=1 \ + ${RPSRC}/mmcsd_no_scr_check-r2.patch;patch=1;status=hack \ ${RPSRC}/integrator_rgb-r1.patch;patch=1;status=hack \ ${RPSRC}/pxa_cf_initorder_hack-r1.patch;patch=1;status=hack \ - ${RPSRC}/corgi_rearrange_lcd-r0.patch;patch=1 \ file://pxa-serial-hack.patch;patch=1;status=hack \ file://connectplus-remove-ide-HACK.patch;patch=1;status=hack \ -# file://squashfs3.2-2.6.20-r0.patch;patch=1;status=external \ + file://connectplus-prevent-oops-HACK.patch;patch=1;status=hack \ # file://htcuni.patch;patch=1 \ file://binutils-buildid-arm.patch;patch=1 \ - file://versatile-armv6.patch;patch=1 \ + file://versatile-armv6.patch;patch=1 \ file://defconfig-c7x0 \ file://defconfig-hx2000 \ file://defconfig-collie \ @@ -84,36 +85,51 @@ SRC_URI = "${KERNELORG_MIRROR}pub/linux/kernel/v2.6/linux-2.6.23.tar.bz2 \ # These patches are extracted from Pavel Machek's git tree # (diff against vanilla kernel) SRC_URI_append_collie = "\ - ${DOSRC}/collie/mtd-sharp-flash-hack-r0.patch;patch=1 \ - ${DOSRC}/collie/collie-r0.patch;patch=1 \ - ${DOSRC}/collie/locomolcd-backlight-r0.patch;patch=1 \ - ${DOSRC}/collie/ucb1x00-touch-audio-r0.patch;patch=1 \ - file://collie-mcp-r1.patch;patch=1 \ - ${DOSRC}/collie/sa1100-udc-r0.patch;patch=1 \ -# ${DOSRC}/collie/collie-pm-r1.patch;patch=1 \ + ${TKSRC}/mtd-sharp-flash-hack-r3.patch;patch=1 \ + ${TKSRC}/mcp-sa11x0-r0.patch;patch=1 \ + ${TKSRC}/locomo-r0.patch;patch=1 \ +# ${TKSRC}/locomo_spi-4.patch;patch=1 \ + ${TKSRC}/collie-kexec.patch;patch=1 \ + ${TKSRC}/sharpsl_pm-3.patch;patch=1 \ + ${TKSRC}/collie_pm-2.patch;patch=1 \ + ${TKSRC}/locomokeyb_suspendkey-2.patch;patch=1 \ + ${TKSRC}/ucb1x00_suspend.patch;patch=1 \ + ${TKSRC}/collie-ts.patch;patch=1 \ + ${TKSRC}/pcmcia_suspend.patch;patch=1 \ +" + +SRC_URI_append_poodle = "\ + ${RPSRC}/poodle_serial_vcc-r0.patch;patch=1 \ " SRC_URI_append_tosa = "\ - ${CHSRC}/usb-ohci-hooks-r1.patch;patch=1 \ ${CHSRC}/tmio-core-r4.patch;patch=1 \ file://tmio-tc6393-r8.patch;patch=1 \ - file://tmio-nand-r7.patch;patch=1 \ - file://tmio-ohci-r6.patch;patch=1 \ + file://tmio-nand-r8.patch;patch=1 \ ${CHSRC}/tmio-fb-r6.patch;patch=1 \ - file://tosa-keyboard-r18.patch;patch=1 \ + file://tmio-fb-r6-fix-r0.patch;patch=1 \ + file://tosa-keyboard-r19.patch;patch=1 \ ${DOSRC}/tosa-pxaac97-r6.patch;patch=1 \ + file://tosa-pxaac97-r6-fix-r0.patch;patch=1 \ ${DOSRC}/tosa-tmio-r6.patch;patch=1 \ - ${DOSRC}/tosa-power-r17.patch;patch=1 \ + file://tosa-power-r18.patch;patch=1 \ + file://tosa-power-r18-fix-r0.patch;patch=1 \ file://tosa-tmio-lcd-r10.patch;patch=1 \ - ${DOSRC}/tosa-bluetooth-r8.patch;patch=1 \ - ${DOSRC}/wm97xx-lg7-r0.patch;patch=1 \ + file://tosa-tmio-lcd-r10-fix-r0.patch;patch=1 \ + file://tosa-bluetooth-r8.patch;patch=1 \ + file://wm97xx-lg13-r0.patch;patch=1 \ + file://wm97xx-lg13-r0-fix-r0.patch;patch=1 \ file://wm9712-suspend-cold-res-r2.patch;patch=1 \ file://sharpsl-pm-postresume-r1.patch;patch=1 \ - ${DOSRC}/wm97xx-dig-restore-r0.patch;patch=1 \ - ${DOSRC}/wm97xx-miscdevs-resume-r0.patch;patch=1 \ file://wm9712-reset-loop-r2.patch;patch=1 \ file://tosa-lcdnoise-r1.patch;patch=1 \ - file://wm97xx-lcdnoise-r0.patch;patch=1 " + file://tosa-lcdnoise-r1-fix-r0.patch;patch=1 \ + file://arm-dma-coherent.patch;patch=1 \ + file://usb-ohci-hooks-r3.patch;patch=1 \ + file://tmio-ohci-r9.patch;patch=1 \ + file://pxa2xx_udc_support_inverse_vbus.patch;patch=1 \ + file://tosa_udc_use_gpio_vbus.patch;patch=1 \ + " # ${DOSRC}/tosa-asoc-r1.patch;patch=1 " SRC_URI_append_htcuniversal ="\ diff --git a/meta/packages/linux/linux-rp_2.6.23.bb b/meta/packages/linux/linux-rp_2.6.23.bb index 2de4ad0cf2..a8c5f79f67 100644 --- a/meta/packages/linux/linux-rp_2.6.23.bb +++ b/meta/packages/linux/linux-rp_2.6.23.bb @@ -1,6 +1,6 @@ require linux-rp.inc -PR = "r25" +PR = "r26" # Handy URLs # git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git;protocol=git;tag=ef7d1b244fa6c94fb76d5f787b8629df64ea4046 @@ -14,10 +14,15 @@ PR = "r25" # Patches submitted upstream are towards top of this list # Hacks should clearly named and at the bottom SRC_URI = "${KERNELORG_MIRROR}pub/linux/kernel/v2.6/linux-2.6.23.tar.bz2 \ - file://hrw-add-wcf11-to-hostap.patch;patch=1;status=pending \ - ${RPSRC}/lzo_jffs2-r3.patch;patch=1 \ - ${RPSRC}/lzo_crypto-r2.patch;patch=1 \ - ${RPSRC}/lzo_jffs2_lzomode-r1.patch;patch=1 \ + ${RPSRC}/pxa25x_suspend_fixes-r0.patch;patch=1;status=merged \ + ${RPSRC}/lzo_jffs2-r3.patch;patch=1;status=merged \ + ${RPSRC}/lzo_jffs2_lzomode-r1.patch;patch=1;status=merged \ + ${RPSRC}/spitzkbd_fix-r0.patch;patch=1;status=merged \ + file://uvesafb-0.1-rc3-2.6.22.patch;patch=1;status=merged \ + ${RPSRC}/locomo_led_fix-r0.patch;patch=1;status=merged \ + file://hrw-add-wcf11-to-hostap.patch;patch=1;status=merged \ + ${RPSRC}/export_atags-r0a.patch;patch=1;status=pending \ + ${RPSRC}/lzo_crypto-r2.patch;patch=1;status=pending \ ${RPSRC}/lzo_jffs2_sysfs-r1.patch;patch=1 \ ${RPSRC}/hx2750_base-r29.patch;patch=1 \ ${RPSRC}/hx2750_bl-r9.patch;patch=1 \ @@ -32,33 +37,27 @@ SRC_URI = "${KERNELORG_MIRROR}pub/linux/kernel/v2.6/linux-2.6.23.tar.bz2 \ ${RPSRC}/usb_add_epalloc-r3.patch;patch=1 \ ${RPSRC}/usb_pxa27x_udc-r6.patch;patch=1 \ ${RPSRC}/locomo_kbd_tweak-r1.patch;patch=1 \ - ${RPSRC}/poodle_pm-r4.patch;patch=1 \ ${RPSRC}/pxa27x_overlay-r6.patch;patch=1 \ - ${RPSRC}/w100_extaccel-r1.patch;patch=1 \ + ${RPSRC}/w100_extaccel-r2.patch;patch=1 \ ${RPSRC}/w100_extmem-r1.patch;patch=1 \ - ${RPSRC}/spitzkbd_fix-r0.patch;patch=1 \ - ${RPSRC}/export_atags-r0.patch;patch=1 \ - ${RPSRC}/pxa25x_suspend_fixes-r0.patch;patch=1 \ + ${RPSRC}/poodle_pm-r4.patch;patch=1 \ ${RPSRC}/poodle_lcd_hack-r0.patch;patch=1 \ ${RPSRC}/poodle_asoc_fix-r1.patch;patch=1 \ - ${RPSRC}/locomo_led_fix-r0.patch;patch=1 \ - file://w100fb-unused-var.patch;patch=1 \ - file://hostap-monitor-mode.patch;patch=1 \ - file://serial-add-support-for-non-standard-xtals-to-16c950-driver.patch;patch=1 \ + file://squashfs3.0-2.6.15.patch;patch=1;status=external \ ${RPSRC}/logo_oh-r1.patch.bz2;patch=1;status=unmergable \ - ${RPSRC}/logo_oz-r2.patch.bz2;patch=1;status=unmergable \ ${RPSRC}/pxa-linking-bug.patch;patch=1;status=unmergable \ + file://hostap-monitor-mode.patch;patch=1;status=unmergable \ + file://serial-add-support-for-non-standard-xtals-to-16c950-driver.patch;patch=1;status=unmergable \ ${RPSRC}/mmcsd_large_cards-r1.patch;patch=1;status=hack \ - file://mmcsd_no_scr_check-r2.patch;patch=1 \ + ${RPSRC}/mmcsd_no_scr_check-r2.patch;patch=1;status=hack \ ${RPSRC}/integrator_rgb-r1.patch;patch=1;status=hack \ ${RPSRC}/pxa_cf_initorder_hack-r1.patch;patch=1;status=hack \ file://pxa-serial-hack.patch;patch=1;status=hack \ file://connectplus-remove-ide-HACK.patch;patch=1;status=hack \ file://connectplus-prevent-oops-HACK.patch;patch=1;status=hack \ - file://squashfs3.0-2.6.15.patch;patch=1;status=external \ - file://uvesafb-0.1-rc3-2.6.22.patch;patch=1;status=external \ file://htcuni.patch;patch=1 \ file://binutils-buildid-arm.patch;patch=1 \ + file://versatile-armv6.patch;patch=1 \ file://defconfig-c7x0 \ file://defconfig-hx2000 \ file://defconfig-collie \ |